Merge remote-tracking branch 'remotes/origin/dev_aliyun' into dev_Cs

dev_hjm
caishi 6 years ago
commit 4005ed56ba

@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

@ -0,0 +1,3 @@
// Place all the styles related to the forums controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

@ -38,6 +38,7 @@ class ApplicationController < ActionController::Base
def user_course_identity
@user_course_identity = current_user.course_identity(@course)
if @user_course_identity > Course::STUDENT && @course.is_public == 0
tip_exception(401, "..") unless User.current.logged?
tip_exception(409, "您没有权限进入")
end
uid_logger("###############user_course_identity:#{@user_course_identity}")
@ -582,4 +583,8 @@ class ApplicationController < ActionController::Base
def render_parameter_missing
render json: { status: -1, message: '参数缺失' }
end
def set_export_cookies
cookies[:fileDownload] = true
end
end

@ -48,7 +48,7 @@ module GitHelper
def project_fork(container, original_rep_path, username)
raise Educoder::TipException.new("fork源路径为空,fork失败!") if original_rep_path.blank?
# 将要生成的仓库名字
new_repo_name = "#{username}/#{container.try(:identifier)}#{ Time.now.strftime("%Y%m%d%H%M%S")}"
new_repo_name = "#{username.try(:strip)}/#{container.try(:identifier)}#{ Time.now.strftime("%Y%m%d%H%M%S")}"
uid_logger("start fork container: repo_name is #{new_repo_name}")
GitService.fork_repository(repo_path: original_rep_path, fork_repository_path: (new_repo_name + ".git"))
container.update_attributes!(:repo_name => new_repo_name)

@ -1,7 +1,7 @@
module PaginateHelper
def paginate(objs, **opts)
page = params[:page].to_i <= 0 ? 1 : params[:page].to_i
per_page = params[:per_page].to_i > 0 ? params[:per_page].to_i : opts[:per_page] || 20
per_page = params[:per_page].to_i > 0 && params[:per_page].to_i < 50 ? params[:per_page].to_i : opts[:per_page] || 20
Kaminari.paginate_array(objs).page(page).per(per_page)
end

@ -1027,10 +1027,13 @@ class CoursesController < ApplicationController
tip_exception(403,"无权限操作")
elsif @all_members.size == 0
normal_status(-1,"课堂暂时没有学生")
elsif params[:export].present? && params[:export]
normal_status(0,"正在下载中")
else
set_export_cookies
member_to_xlsx(@course, @all_members, @c_homeworks, @c_exercises, @c_tasks)
filename_ = "#{current_user.real_name}_#{@course.name}_全部成绩"
render xlsx: "#{format_sheet_name filename_.strip.first(30)}",template: "courses/export_member_scores_excel.xlsx.axlsx",
filename_ = "#{current_user.real_name}_#{@course.name}_全部成绩_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
render xlsx: "#{format_sheet_name filename_.strip}",template: "courses/export_member_scores_excel.xlsx.axlsx",
locals: {course_info:@course_info, activity_level:@user_activity_level,
course_scores:@course_user_scores,shixun_works:@shixun_work_arrays,
common_works:@common_work_arrays,group_works:@group_work_arrays,task_works:@task_work_arrays,

@ -1256,13 +1256,16 @@ class ExercisesController < ApplicationController
normal_status(-1,"试卷未发布")
elsif (@exercise_users_size == 0) || ( @export_ex_users&.exercise_user_committed.size == 0)
normal_status(-1,"暂无用户提交")
elsif params[:export].present? && params[:export]
normal_status(0,"正在下载中")
else
respond_to do |format|
format.xlsx{
set_export_cookies
get_export_users(@exercise,@course,@export_ex_users)
exercise_export_name_ =
"#{current_user.real_name}_#{@course.name}_#{@exercise.exercise_name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
render xlsx: "#{exercise_export_name_.strip.first(30)}",template: "exercises/exercise_lists.xlsx.axlsx",locals: {table_columns:@table_columns,exercise_users:@user_columns}
render xlsx: "#{exercise_export_name_.strip}",template: "exercises/exercise_lists.xlsx.axlsx",locals: {table_columns:@table_columns,exercise_users:@user_columns}
}
end
end
@ -1281,7 +1284,12 @@ class ExercisesController < ApplicationController
@exercise_questions = @exercise.exercise_questions.includes(:exercise_choices).order("question_number ASC")
filename_ = "#{@exercise.user.real_name}_#{@course.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}.pdf"
stylesheets = "#{Rails.root}/app/templates/exercise_export/exercise_export.css"
render pdf: 'exercise_export/blank_exercise', filename: filename_, stylesheets: stylesheets
if params[:export].present? && params[:export]
normal_status(0,"正在下载中")
else
set_export_cookies
render pdf: 'exercise_export/blank_exercise', filename: filename_, stylesheets: stylesheets
end
end
#空白试卷预览页面,仅供测试使用,无其他任何用途

@ -0,0 +1,2 @@
class ForumsController < ApplicationController
end

@ -116,10 +116,11 @@ class GamesController < ApplicationController
@qrcode_str = Base64.encode64( qr.to_img.resize(400,400).to_s )
else
@type = "image"
#conv = Iconv.new("GBK", "utf-8")
@game_challenge = @game.challenge
type = @game_challenge.show_type
@type = shixun_show_type type
workspace_path = @game.try(:picture_path)
@answer_path = "#{Rails.root}/#{workspace_path}/#{@game_challenge.expect_picture_path}"
@user_path = "#{Rails.root}/#{workspace_path}/#{@game_challenge.picture_path}"

@ -132,12 +132,14 @@ class GraduationTasksController < ApplicationController
tip_exception(403, "无权限操作")
elsif complete_works == 0
normal_status(-1,"暂无用户提交")
elsif params[:export].present? && params[:export]
normal_status(0,"正在下载中")
else
respond_to do |format|
format.xlsx{
graduation_work_to_xlsx(@work_excel,@task,current_user)
task_export_name_ = "#{current_user.real_name}_#{@course.name}_#{@task.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
render xlsx: "#{task_export_name_.strip.first(30)}",template: "graduation_tasks/tasks_list.xlsx.axlsx",locals: {table_columns:@head_cells_column, task_users:@task_cells_column}
render xlsx: "#{task_export_name_.strip}",template: "graduation_tasks/tasks_list.xlsx.axlsx",locals: {table_columns:@head_cells_column, task_users:@task_cells_column}
}
end
end
@ -148,12 +150,17 @@ class GraduationTasksController < ApplicationController
zip_works = @work_excel.where("work_status > 0")
status = checkfileSize(zip_works)
if status == 0
respond_to do |format|
format.zip{
zipfile = zip_homework_common @task, zip_works
file = decode64(zipfile[0][:base64file])
send_file "#{OUTPUT_FOLDER}/#{file}", filename: filename_for_content_disposition(file), type: 'application/zip'
}
if params[:export].present? && params[:export]
normal_status(0,"正在下载中")
else
respond_to do |format|
format.zip{
set_export_cookies
zipfile = zip_homework_common @task, zip_works
file = decode64(zipfile[0][:base64file])
send_file "#{OUTPUT_FOLDER}/#{file}", filename: filename_for_content_disposition(file), type: 'application/zip'
}
end
end
else
normal_status(status,status == -2 ? "500M" : "无附件可下载")

@ -271,7 +271,12 @@ class GraduationTopicsController < ApplicationController
students = course.students.joins(user: :user_extension).order("user_extensions.student_id")
graduation_topic_to_xlsx(students,course)
topic_export_name_ = "#{current_user.real_name}_#{course.name}_毕设选题_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
render xlsx: "#{topic_export_name_.strip.first(30)}",template: "graduation_topics/export.xlsx.axlsx",locals: {table_columns:@topic_head_cells,topic_users:@topic_body_cells}
if params[:export].present? && params[:export]
normal_status(0,"正在下载中")
else
set_export_cookies
render xlsx: "#{topic_export_name_.strip}",template: "graduation_topics/export.xlsx.axlsx",locals: {table_columns:@topic_head_cells,topic_users:@topic_body_cells}
end
rescue Exception => e
uid_logger(e.message)
missing_template

@ -207,12 +207,15 @@ class HomeworkCommonsController < ApplicationController
tip_exception(403, "无权限操作")
elsif @work_excel.blank? || @work_excel.size == 0
normal_status(-1,"暂无用户提交!")
elsif params[:export].present? && params[:export]
normal_status(0,"正在下载中")
else
respond_to do |format|
format.xlsx{
set_export_cookies
student_work_to_xlsx(@work_excel,@homework)
exercise_export_name = "#{current_user.real_name}_#{@course.name}_#{@homework.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
render xlsx: "#{exercise_export_name.strip.first(30)}",template: "homework_commons/works_list.xlsx.axlsx",locals:
render xlsx: "#{exercise_export_name.strip}",template: "homework_commons/works_list.xlsx.axlsx",locals:
{table_columns: @work_head_cells,task_users: @work_cells_column}
}
end
@ -229,12 +232,17 @@ class HomeworkCommonsController < ApplicationController
end
if status == 0
respond_to do |format|
format.zip{
zipfile = zip_homework_common @homework, zip_works
file = decode64(zipfile[0][:base64file])
send_file "#{OUTPUT_FOLDER}/#{file}", filename: filename_for_content_disposition(file), type: 'application/zip'
}
if params[:export].present? && params[:export]
normal_status(0,"正在下载中")
else
respond_to do |format|
format.zip{
set_export_cookies
zipfile = zip_homework_common @homework, zip_works
file = decode64(zipfile[0][:base64file])
send_file "#{OUTPUT_FOLDER}/#{file}", filename: filename_for_content_disposition(file), type: 'application/zip'
}
end
end
else
normal_status(status, status == -2 ? "500M" : "无附件可下载")

@ -66,9 +66,7 @@ class MemosController < ApplicationController
# GET /memos/new
def new
@csrf_token = session[:_csrf_toke] ||= SecureRandom.base64(32)
@tag_list = TagRepertoire.field_for_list.order("name asc")
end
# GET /memos/1/edit

@ -24,7 +24,7 @@ class MyshixunsController < ApplicationController
ActiveRecord::Base.transaction do
begin
@shixun = Shixun.select(:id, :identifier).find(@myshixun.shixun_id)
@myshixun.destroy
@myshixun.destroy!
StudentWork.where(:myshixun_id => @myshixun.id).update_all(:myshixun_id => 0, :work_status => 0)

@ -945,12 +945,15 @@ class PollsController < ApplicationController
tip_exception(403,"无权限操作")
elsif (@poll.polls_status == 1) || (@poll_export_questions.size == 0) || (@poll_commit_ids.size == 0)
normal_status(-1,"暂无用户提交")
elsif params[:export].present? && params[:export]
normal_status(0,"正在下载中")
else
respond_to do |format|
format.xlsx{
set_export_cookies
polls_export_name_ = "#{current_user.real_name}_#{@course.name}_#{@poll.polls_name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
polls_user_commit = poll_commit_result(@poll,@poll_export_questions,@poll_users,@poll_commit_ids)
render xlsx: "#{polls_export_name_.strip.first(30)}",template: "polls/commit_result.xlsx.axlsx",locals: {polls_user_commit:polls_user_commit}
render xlsx: "#{polls_export_name_.strip}",template: "polls/commit_result.xlsx.axlsx",locals: {polls_user_commit:polls_user_commit}
}
end
end

@ -0,0 +1,5 @@
class Projects::BaseController < ApplicationController
include PaginateHelper
before_action :require_login, :check_auth
end

@ -0,0 +1,14 @@
class Projects::ProjectAppliesController < Projects::BaseController
def create
project = Projects::ApplyJoinService.call(current_user, create_params)
render_ok(project_id: project.id)
rescue Projects::ApplyJoinService::Error => ex
render_error(ex.message)
end
private
def create_params
params.permit(:code, :role)
end
end

@ -714,9 +714,10 @@ class ShixunsController < ApplicationController
end
end
rescue Exception => e
logger.info("shixun_exec error: #{e.message}")
if e.message != "ActiveRecord::RecordInvalid"
logger.error("##########project_fork error #{e.message}")
@current_task.destroy!
@myshixun.destroy!
end
raise "实训云平台繁忙繁忙等级81"
end

@ -457,8 +457,8 @@ class StudentWorksController < ApplicationController
@myself_eff = @echart_data[:efficiency_list].find { |item| item.last == @user.id }
@myself_consume = @echart_data[:consume_list].find { |item| item.last == @user.id }
filename_ = "实训报告_#{@shixun&.name}_#{@use&.real_name}"
filename = Base64.urlsafe_encode64(filename_.strip.first(30))
filename_ = "#{@use&.student_id}_#{@use&.real_name}_#{@shixun&.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
filename = Base64.urlsafe_encode64(filename_.strip)
stylesheets = %w(shixun_work/shixun_work.css shared/codemirror.css)
render pdf: 'shixun_work/shixun_work', filename: filename, stylesheets: stylesheets
end

@ -26,9 +26,22 @@ class Users::BaseController < ApplicationController
render_forbidden
end
def page_value
params[:page].to_i <= 0 ? 1 : params[:page].to_i
end
def per_page_value
params[:per_page].to_i > 0 && params[:per_page].to_i < 50 ? params[:per_page].to_i : 20
end
alias_method :limit_value, :per_page_value
def offset_value
(page_value - 1) * limit_value
end
def paginate(objs, **opts)
page = params[:page].to_i <= 0 ? 1 : params[:page].to_i
per_page = params[:per_page].to_i > 0 ? params[:per_page].to_i : 20
page = page_value
per_page = per_page_value
return Kaminari.paginate_array(objs).page(page).per(per_page) unless observed_logged_user? && opts[:special]

@ -0,0 +1,23 @@
class Users::PrivateMessageDetailsController < Users::BaseController
before_action :private_user_resources!
after_action :update_message_status, only: [:show]
def show
messages = observed_user.private_messages.without_deleted.where(target: target_user)
@count = messages.count
@messages = messages.order(send_time: :asc).includes(sender: :user_extension)
end
private
def target_user
@_target_user ||= User.find(params[:target_id])
end
# 置为已读
def update_message_status
observed_user.private_messages.only_unread.where(target: target_user).update_all(status: 1)
end
end

@ -0,0 +1,39 @@
class Users::PrivateMessagesController < Users::BaseController
before_action :private_user_resources!
after_action :update_onclick_time!, only: [:index]
def index
@count = observed_user.private_messages.without_deleted.group(:target_id).count.count
subquery = observed_user.private_messages.without_deleted.order(send_time: :desc).to_sql
query = "SELECT subquery.*, COUNT(*) message_count FROM (#{subquery}) subquery "\
"GROUP BY subquery.target_id ORDER BY subquery.send_time desc LIMIT #{limit_value} OFFSET #{offset_value}"
@messages = PrivateMessage.select('*').from("(#{query}) AS query").includes(target: :user_extension)
end
def create
receiver = User.find_by(id: params[:target_id])
return render_error('用户未找到') if receiver.blank?
@message = PrivateMessages::CreateService.call(observed_user, receiver, create_params)
rescue PrivateMessages::CreateService::Error => ex
render_error(ex.message)
end
def destroy
message = observed_user.private_messages.without_deleted.find(params[:id])
message.destroy!
render_ok
end
private
def update_onclick_time!
current_user.onclick_time.touch(:onclick_time)
end
def create_params
params.permit(:content)
end
end

@ -0,0 +1,8 @@
class Users::RecentContactsController < Users::BaseController
before_action :private_user_resources!
def index
contacts = observed_user.recent_contacts.distinct
@contacts = contacts.order('private_messages.created_at DESC').limit(10).includes(:user_extension)
end
end

@ -0,0 +1,12 @@
class Users::UnreadMessageInfosController < Users::BaseController
before_action :private_user_resources!
def show
click_time = observed_user.click_time
unread_tiding_count = observed_user.tidings.where('created_at > ?', click_time).count
unread_message_count = observed_user.private_messages.only_unread.group(:target_id).count.count
render_ok(unread_tiding_count: unread_tiding_count, unread_message_count: unread_message_count)
end
end

@ -73,8 +73,7 @@ class UsersController < ApplicationController
@user_url = "/users/#{@user.login}"
@career = Career.where(status: true).order("created_at asc").pluck(:id, :name)
ec_user = EcSchoolUser.where(:user_id => current_user.id).first
@auth = ec_user ? "#{@old_domain}/ecs/department?school_id=#{ec_user.school_id}" : nil
@auth = User.current.ec_school.present? ? "#{@old_domain}/ecs/department?school_id=#{User.current.ec_school}" : nil
end
# 用户回复功能

@ -0,0 +1,17 @@
class UsersForPrivateMessagesController < ApplicationController
before_action :require_login, :check_auth
def index
users = User.active.where.not(id: current_user.id)
keyword = params[:keyword].to_s.strip
if keyword.blank?
@users = []
return
end
users = users.where('LOWER(concat(lastname, firstname, nickname)) LIKE ?', "%#{keyword}%")
@users = users.limit(10).includes(:user_extension)
end
end

@ -8,7 +8,13 @@ class ZipsController < ApplicationController
def shixun_report
service = BatchExportShixunReportService.new(@homework, @all_student_works)
filename_ = filename_for_content_disposition(service.filename)
send_file service.zip, filename: filename_, type: 'application/zip'
if params[:export].present? && params[:export]
normal_status(0,"正在下载中")
else
set_export_cookies
send_file service.zip, filename: filename_, type: 'application/zip'
end
rescue BatchExportShixunReportService::Error => ex
normal_status(-1, ex.message)
end
@ -18,7 +24,12 @@ class ZipsController < ApplicationController
exercises = ExportExercisesService.new(@exercise,@ex_users,@request_url)
file_name_ = filename_for_content_disposition(exercises.filename)
send_file exercises.ex_zip, filename: file_name_, type: 'application/zip'
if params[:export].present? && params[:export]
normal_status(0,"正在下载中")
else
set_export_cookies
send_file exercises.ex_zip, filename: file_name_, type: 'application/zip'
end
rescue Exception => e
normal_status(-1, e.message)
end

@ -0,0 +1,9 @@
module PrivateMessageDecorator
extend ApplicationDecorator
display_time_method :send_time
def unread?
status.zero?
end
end

@ -98,7 +98,7 @@ module TidingDecorator
end
def apply_add_schools_content
name = container.name
name = ApplyAddSchool.find_by(id: container_id)&.name
if tiding_type == 'Apply'
I18n.t(locale_format(tiding_type)) % name
elsif status == 2
@ -201,7 +201,7 @@ module TidingDecorator
when 'Issue' then
I18n.t(locale_format(parent_container_type)) % parent_container.subject
when 'Journal' then
message = object.notes.present? ? '' + message_content_helper(parent_container.notes) : ''
message = parent_container&.notes.present? ? '' + message_content_helper(parent_container.notes) : ''
I18n.t(locale_format(parent_container_type)) % message
end
end
@ -331,13 +331,17 @@ module TidingDecorator
end
def challenge_work_score_content
I18n.t(locale_format) % container&.comment
end
def student_works_scores_appeal_content
work = StudentWork.find_by(id: parent_container_id)
return if work.blank?
name = work&.homework_common&.name
if parent_container_type == 'StudentWork'
I18n.t(locale_format(parent_container_type, tiding_type)) % work.homework_common.try(:name)
elsif parent_container_type == 'UserAppealResult' || parent_container_type == 'AppealResult'
I18n.t(locale_format(parent_container_type, status)) % work.homework_common.try(:name)
I18n.t(locale_format(parent_container_type, tiding_type)) % name
else
I18n.t(locale_format(parent_container_type, status)) % name
end
end
@ -349,7 +353,7 @@ module TidingDecorator
if tiding_type == 'System'
I18n.t(locale_format(tiding_type, status), reason: extra) % container.try(:title)
else
I18n.t(locale_format) % container.try(:title)
I18n.t(locale_format(tiding_type)) % container.try(:title)
end
end

@ -418,7 +418,7 @@ module ExportHelper
end
end
out_file_name = "#{Time.now.to_i}_#{homework_common.name}.zip"
out_file_name = "作品附件_#{homework_common&.course&.name}_#{homework_common.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}.zip"
out_file_name.gsub!(" ", "-")
out_file_name.gsub!("/", "_")
out_file = find_or_pack(homework_common, homework_common.user_id, digests.sort){
@ -496,8 +496,8 @@ module ExportHelper
def make_zip_name(work, file_name="")
Rails.logger.info("######################file_name: #{file_name}")
name = file_name === "" ? "" : (file_name[0, file_name.rindex('.')]+"_")
"#{name}#{work.user.real_name}_#{((work.user.student_id.nil?) ? "" : work.user.student_id)}"
# name = file_name === "" ? "" : (file_name[0, file_name.rindex('.')]+"_")
"#{work&.user&.student_id}_#{work&.user&.real_name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
end
def zipping(zip_name_refer, files_paths, output_path, is_attachment=false, not_exist_file=[])

@ -0,0 +1,2 @@
module ForumsHelper
end

@ -6,7 +6,7 @@ module GamesHelper
end
# 获取目录下所有文件,返回一个文件名的数组 type是查看文件的类型image表示图片
# type [[1, "图片"], [2, "apk/exe"], [3, "txt"], [4, "html"]]
# type [[1, "图片"], [2, "apk/exe"], [3, "txt"], [4, "html"], [5, "mp3"], [6, "mp4"]]
def get_dir_filename(path, type, game_id)
answer_picture = []
return answer_picture unless File.directory?(path)
@ -39,6 +39,12 @@ module GamesHelper
end
f.close
@type = 'txt'
elsif extension == 'mp3' && type == 5
answer_picture << file
@type = 'mp3'
elsif extension == 'mp4' && type == 6
answer_picture << file
@type = 'mp4'
end
end
@ -51,4 +57,21 @@ module GamesHelper
"编译失败,请在测试结果中查看具体的错误信息" : test_set.try(:actual_output)
end
end
def shixun_show_type type
case type.to_i
when 1
"image"
when 2
"apk/exe"
when 3
"txt"
when 4
"html"
when 5
"mp3"
when 6
"mp4"
end
end
end

@ -1,2 +1,6 @@
module MemosHelper
def forum_list
[{id: 5, name: "技术分享"}, {id: 3, name: "操作指南"}]
end
end

@ -0,0 +1,31 @@
# 申请成为 管理员、开发者 加入项目 消息通知
class ApplyJoinProjectNotifyJob < ApplicationJob
queue_as :notify
def perform(user_id, project_id, role)
user = User.find_by(id: user_id)
project = Project.find_by(id: project_id)
return if user.blank? || project.blank?
attrs = %i[user_id trigger_user_id container_id container_type status
belong_container_id belong_container_type tiding_type extra created_at updated_at]
same_attrs = {
trigger_user_id: user.id, status: 0, tiding_type: 'Apply', extra: role,
container_id: project.id, container_type: 'JoinProject',
belong_container_id: project.id, belong_container_type: 'Project'
}
# 报告人员加入时消息为系统通知消息
if role == 5
same_attrs[:container_type] = 'ReporterJoinProject'
same_attrs[:tiding_type] = 'System'
end
Tiding.bulk_insert(*attrs) do |worker|
project.manager_members.each do |manager|
worker.add(same_attrs.merge(user_id: manager.user_id))
end
end
end
end

@ -0,0 +1,9 @@
class AppliedProject < ApplicationRecord
belongs_to :user
belongs_to :project
has_many :applied_messages, as: :applied, dependent: :destroy
has_many :forge_activities, as: :forge_act, dependent: :destroy
scope :pending, -> { where(status: 0) }
end

@ -1,5 +1,6 @@
class Challenge < ApplicationRecord
# difficulty: 关卡难度: 1.简单 2.中等 3.困难
# show_type: 效果展示:-1.无效果 1.图片 2.apk/exe 3.txt 4.html 5.mp3 6.mp4
default_scope { order("challenges.position asc") }
belongs_to :shixun, :touch => true, counter_cache: true

@ -0,0 +1,5 @@
class ForgeActivity < ApplicationRecord
belongs_to :user
belongs_to :project
belongs_to :forge_act, polymorphic: true
end

@ -0,0 +1,2 @@
class Forum < ApplicationRecord
end

@ -0,0 +1,4 @@
class Journal < ApplicationRecord
belongs_to :user
belongs_to :issue, foreign_key: :journalized_id
end

@ -1,6 +1,8 @@
class Member < ApplicationRecord
has_many :member_roles, dependent: :destroy
belongs_to :user
belongs_to :course, optional: true
belongs_to :project, optional: true
belongs_to :user
has_many :member_roles, dependent: :destroy
has_many :roles, through: :member_roles
end

@ -1,3 +1,4 @@
class MemberRole < ApplicationRecord
belongs_to :role
belongs_to :member
end

@ -1,7 +1,9 @@
class Memo < ApplicationRecord
include Searchable::Memo
has_many :memo_tag_repertoires, :dependent => :destroy
belongs_to :forum, touch: true
has_many :memo_tag_repertoires, dependent: :destroy
has_many :tag_repertoires, :through => :memo_tag_repertoires
has_many :praise_tread, as: :praise_tread_object, dependent: :destroy

@ -1,3 +1,9 @@
class PrivateMessage < ApplicationRecord
belongs_to :user
belongs_to :target, class_name: "User"
belongs_to :sender, class_name: "User"
belongs_to :receiver, class_name: "User"
scope :without_deleted, -> { where.not(status: 2) }
scope :only_unread, -> { where(status: 0) }
end

@ -1,9 +1,12 @@
class Project < ApplicationRecord
belongs_to :owner, class_name: 'User', foreign_key: :user_id
has_many :members
has_many :manager_members, -> { joins(:roles).where(roles: { name: 'Manager' }) }, class_name: 'Member'
has_one :project_score, dependent: :destroy
has_many :issues
has_many :user_grades, dependent: :destroy
# 创建者
def creator

@ -0,0 +1,3 @@
class Role < ApplicationRecord
has_many :member_roles, dependent: :destroy
end

@ -8,7 +8,7 @@ module Searchable::Dependents::User
private
def check_searchable_dependents
if firstname_previously_changed? || lastname_previously_changed? || user_extension.school_id_previously_changed?
if firstname_previously_changed? || lastname_previously_changed? || user_extension&.school_id_previously_changed?
# reindex shixun
created_shixuns.each(&:reindex)

@ -6,4 +6,21 @@ class Tiding < ApplicationRecord
belongs_to :belong_container, polymorphic: true, optional: true
has_many :attachments, as: :container
def identifier
value = nil
if Object.const_defined?(container_type)
value = container.try(:identifier)
end
if value.blank? && parent_container_type && Object.const_defined?(parent_container_type)
value = parent_container_type.try(:identifier)
end
if value.blank? && belong_container_type && Object.const_defined?(belong_container_type)
value = belong_container.try(:identifier)
end
value
end
end

@ -54,7 +54,8 @@ class User < ApplicationRecord
has_one :onclick_time, :dependent => :destroy
# 新版私信
has_many :private_messages, :dependent => :destroy
has_many :private_messages, dependent: :destroy
has_many :recent_contacts, through: :private_messages, source: :target
has_many :tidings, :dependent => :destroy
has_many :games, :dependent => :destroy
@ -128,6 +129,9 @@ class User < ApplicationRecord
has_many :bidding_users, dependent: :destroy
has_many :bidden_project_packages, through: :bidding_users, source: :project_package
# 项目
has_many :applied_projects, dependent: :destroy
# Groups and active users
scope :active, lambda { where(status: STATUS_ACTIVE) }
@ -497,6 +501,13 @@ class User < ApplicationRecord
)
end
# 工程认证的学校
def ec_school
school_id = self.ec_school_users.pluck(:school_id).first ||
self.ec_major_schools.pluck(:school_id).first ||
(self.ec_course_users.first && self.ec_course_users.first.try(:ec_course).try(:ec_year).try(:ec_major_school).try(:school_id))
end
# 登录,返回用户名与密码匹配的用户
def self.try_to_login(login, password)
login = login.to_s.strip

@ -0,0 +1,4 @@
class UserGrade < ApplicationRecord
belongs_to :project
belongs_to :user
end

@ -14,7 +14,7 @@ class BatchExportShixunReportService
end
def filename
@_filename ||= "#{homework.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
@_filename ||= "实训报告_#{homework&.course&.name}_#{homework.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
end
def zip

@ -22,6 +22,7 @@ module ElasticsearchAble
fragment_size: EduSetting.get('es_highlight_fragment_size') || 30,
tag: '<span class="highlight">',
fields: {
name: { type: 'plain' },
challenge_names: { type: 'plain' },
challenge_tag_names: { type: 'plain' },
description: { type: 'plain' },

@ -15,8 +15,8 @@ class ExerciseUserPdfService
end
def filename
user_course = @course.course_members.find_by(user_id:@ex_user_user.id)&.course_group_name
exercise_user_name = "#{@ex_user_user.real_name}_#{user_course.present? ? user_course : "未分班"}_#{exercise.exercise_name}_#{Time.now.strftime('%Y%m%d_%H%M')}"
# user_course = @course.course_members.find_by(user_id:@ex_user_user.id)&.course_group_name
exercise_user_name = "#{@ex_user_user&.student_id}_#{@ex_user_user.real_name}_#{exercise.exercise_name}_#{Time.now.strftime('%Y%m%d_%H%M')}"
"#{exercise_user_name.strip}.pdf"
end

@ -10,7 +10,7 @@ class ExportExercisesService
end
def filename
exercise_export_name = "#{exercise.user.real_name}_#{exercise.exercise_name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
exercise_export_name = "学生答题_#{exercise&.course&.name}_#{exercise.exercise_name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
"#{exercise_export_name.strip}.zip"
end

@ -10,7 +10,7 @@ class ExportShixunReportService
end
def filename
@_filename ||= "#{homework.name}-#{work.user&.student_id}-#{work.user.real_name}.pdf".gsub(' ', '-').gsub('/', '_')
@_filename ||= "#{work.user&.student_id}_#{work.user.real_name}_#{homework.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}.pdf".gsub(' ', '-').gsub('/', '_')
end
def prepare_binding

@ -0,0 +1,35 @@
class PrivateMessages::CreateService < ApplicationService
Error = Class.new(StandardError)
attr_reader :sender, :receiver, :params
def initialize(sender, receiver, **params)
@sender = sender
@receiver = receiver
@params = params
end
def call
validate!
same_attr = { sender: sender, receiver: receiver, content: content, send_time: Time.now }
message = nil
ActiveRecord::Base.transaction do
message = sender.private_messages.create!(same_attr.merge(target: receiver, status: 1))
receiver.private_messages.create!(same_attr.merge(target: sender, status: 0))
end
message
end
private
def content
@_content ||= params[:content].to_s.strip
end
def validate!
raise Error, '内容不能为空' if content.blank?
raise Error, '内容太长' if content.size > 255
end
end

@ -15,7 +15,7 @@ class ProjectPackages::SaveService < ApplicationService
is_create = package.new_record?
raise Error, '类型不存在' unless ProjectPackageCategory.where(id: params[:category_id]).exists?
params[:project_package_category_id] = params[:category_id].to_i
params[:project_package_category_id] = params.delete(:category_id).to_i
raise Error, '竞标截止时间不能小于当前时间' if params[:deadline_at].present? && params[:deadline_at].to_time < Time.now
@ -25,7 +25,9 @@ class ProjectPackages::SaveService < ApplicationService
end
ActiveRecord::Base.transaction do
package.assign_attributes(params)
columns = %i[project_package_category_id title content deadline_at
min_price max_price contact_name contact_phone]
package.assign_attributes(params.slice(*columns))
package.save!
# 处理附件

@ -0,0 +1,82 @@
class Projects::ApplyJoinService < ApplicationService
Error = Class.new(StandardError)
attr_reader :user, :params
def initialize(user, params)
@user = user
@params = params
end
def call
validate!
# 项目报告人员直接加入项目
if params[:role] == 'reporter'
Projects::JoinService.call(project, user, role: 'reporter')
return project
end
ActiveRecord::Base.transaction do
apply = user.applied_projects.create!(project: project, role: role_value)
apply.forge_activities.find_or_create_by!(user: user, project: project)
notify_project_manager!
end
# notify_project_owner
ApplyJoinProjectNotifyJob.perform_later(user.id, project.id, role_value)
project
end
private
def project
@_project ||= Project.find_by(invite_code: params[:code].to_s.strip)
end
def role_value
@_role ||=
case params[:role]
when 'manager' then 3
when 'developer' then 4
when 'reporter' then 5
else raise Error, '角色无效'
end
end
def notify_project_manager!
columns = %i[user_id applied_id applied_type status viewed applied_user_id role project_id created_at updated_at]
AppliedMessage.bulk_insert(*columns) do |worker|
base_attr = { status: false, viewed: false, applied_user_id: user.id, role: role_value, project_id: project.id }
project.manager_members.each do |manager|
worker.add(base_attr.merge(user_id: manager.user_id))
end
end
end
def notify_project_owner
owner = project.user
return if owner.phone.blank?
Educoder::Sms.send(mobile: owner.phone, send_type:'applied_project_info',
user_name: owner.show_name, name: project.name)
rescue Exception => ex
Rails.logger.error("发送短信失败 => #{ex.message}")
end
def validate!
# params check
raise Error, '邀请码不能为空' if params[:code].blank?
raise Error, '角色不能为空' if params[:role].blank?
raise Error, '角色无效' unless %w(manager developer reporter).include?(params[:role])
# logical check
raise Error, '邀请码无效' if project.blank?
raise Error, '您已在该项目中' if project.member?(user)
raise Error, '您已经提交过申请' if user.applied_projects.pending.exists?(project: project)
end
end

@ -0,0 +1,35 @@
class Projects::JoinService < ApplicationService
attr_reader :project, :user, :opts
def initialize(project, user, **opts)
@project = project
@user = user
@opts = opts
end
def call
ActiveRecord::Base.transaction do
member = project.members.create!(user: user)
member.member_roles.create!(role_id: role_value)
project.user_grades.find_or_create_by!(user: user)
end
ApplyJoinProjectNotifyJob.perform_later(user, project, role_value)
project
end
private
def role_value
@_role ||=
case opts[:role]
when 'manager' then 3
when 'developer' then 4
when 'reporter' then 5
else raise ArgumentError
end
end
end

@ -1,6 +1,7 @@
json.users do
json.array! @users do |user|
json.id user.id
json.login user.login
json.name user.real_name
json.student_id user&.student_id
json.school_name user&.school_name

@ -24,5 +24,30 @@ elsif @type == "txt"
json.contents @contents.html_safe
elsif @type =="qrcode"
json.qrcode_str @qrcode_str
elsif @type == "mp3" || @type == "mp4"
if @type == "mp4"
json.orignal_file [{"file_url": "http://120.27.231.56:48080/attachments/download/378171/123.mp4"}]
json.user_file [{"file_url": "http://120.27.231.56:48080/attachments/download/378172/456.mp4"}]
json.answer_file [{"file_url": "http://120.27.231.56:48080/attachments/download/378173/789.mp4"}]
else
json.orignal_file [{"file_url": "http://120.27.231.56:48080/attachments/download/378174/58099.mp3"}]
json.user_file [{"file_url": "http://120.27.231.56:48080/attachments/download/378175/654058514.mp3"}]
json.answer_file [{"file_url": "http://120.27.231.56:48080/attachments/download/378175/654058514.mp3"}]
end
# json.orignal_file do
# json.array! @orignal_picture do |file|
# json.file_url attachment_show_users_path(:file_name => file, :path => @original_path)
# end
# end
# json.user_file do
# json.array! @user_picture do |file|
# json.file_url attachment_show_users_path(:file_name => file, :path => @user_path, :time => Time.now.to_i)
# end
# end
# json.answer_file do
# json.array! @answer_picture do |file|
# json.file_url attachment_show_users_path(:file_name => file, :path => @answer_path)
# end
# end
end

@ -1,3 +1,3 @@
json.tag_list @tag_list
json.csrf_token @csrf_token
json.forums @csrf_token

@ -1,6 +1,6 @@
json.extract! tiding, :id, :status, :viewed, :user_id, :tiding_type, :container_id, :container_type, :parent_container_id, :parent_container_type
json.content tiding.content
json.identifier tiding.try(:container).try(:identifier) rescue nil
json.identifier tiding.identifier
json.time tiding.how_long_time
json.new_tiding tiding.unread?(@onclick_time)

@ -7,6 +7,7 @@ json.is_teacher @user.user_extension&.teacher?
json.user_identity @user.identity
json.tidding_count 0
json.user_phone_binded @user.phone.present?
json.phone @user.phone
json.profile_completed @user.profile_completed?
if @course
json.course_identity @course_identity

@ -0,0 +1,11 @@
json.count @count
json.messages do
json.array! @messages.each do |message|
json.extract! message, :id, :user_id, :receiver_id, :sender_id, :content
json.send_time message.display_send_time
json.sender do
json.partial! 'users/user_simple', user: message.sender
end
end
end

@ -0,0 +1,10 @@
json.status 0
json.message 'success'
json.private_message do
json.extract! @message, :id, :user_id, :receiver_id, :sender_id, :content
json.send_time @message.display_send_time
json.sender do
json.partial! 'users/user_simple', user: @message.sender
end
end

@ -0,0 +1,13 @@
json.count @count
json.private_messages do
json.array! @messages.each do |message|
json.extract! message, :id, :content, :message_count
json.unread message.unread?
json.send_time message.display_send_time
json.target do
json.partial! 'users/user_simple', user: message.target
end
end
end

@ -0,0 +1,2 @@
json.users @contacts, partial: 'users/user_simple', as: :user
json.count @contacts.size

@ -0,0 +1,2 @@
json.users @users, partial: 'users/user_simple', as: :user
json.count @users.size

@ -53,6 +53,11 @@ Rails.application.routes.draw do
resource :grade_records, only: [:show]
resource :watch, only: [:create, :destroy]
resources :project_packages, only: [:index]
# 私信
resources :private_messages, only: [:index, :create, :destroy]
resources :recent_contacts, only: [:index]
resource :private_message_details, only: [:show]
resource :unread_message_info, only: [:show]
end
@ -91,6 +96,7 @@ Rails.application.routes.draw do
end
end
end
resources :users_for_private_messages, only: [:index]
resources :myshixuns, param: :identifier, shallow: true do
member do
@ -695,6 +701,10 @@ Rails.application.routes.draw do
end
resources :libraries, only: [:index, :show, :create, :update, :destroy]
scope module: :projects do
resources :applied_projects, only: [:create]
end
end
#git 认证回调

@ -0,0 +1,9 @@
class ModifyLoginForUsers < ActiveRecord::Migration[5.2]
def change
users = User.where("created_on > '2019-07-26 19:00:00'")
users.find_each do |use|
use.update_attributes(login: use.login&.strip, phone: use.phone&.strip)
use.user_extension.update_column(:student_id, use.user_extension&.student_id&.strip) if use.user_extension
end
end
end

@ -0,0 +1,11 @@
class DeleteErrorMyshixunFromMyshxiuns < ActiveRecord::Migration[5.2]
def change
myshixuns = Myshixun.where("created_at > '2019-07-26 00:00:00' and repo_name is null")
myshixuns.find_each do |myshixun|
if myshixun.games.blank?
puts("###########user_login: #{User.find(myshixun.user_id).login}")
myshixun.destroy!
end
end
end
end

@ -0,0 +1,11 @@
class DeleteMyshixunGamesForUsers < ActiveRecord::Migration[5.2]
def change
myshixuns = Myshixun.where("created_at > '2019-07-26 19:00:00' and repo_name is null")
myshixuns.find_each do |m|
if m.games.count == m.games.select{|g| g.status == 3}.count
puts("#######login: #{User.find(m.user_id).login}")
m.destroy!
end
end
end
end

@ -0,0 +1,7 @@
class AddIndexToUser < ActiveRecord::Migration[5.2]
def change
# add_index :users, :login, unique: true
# add_index :users, :mail, unique: true
# add_index :users, :phone, unique: true
end
end

@ -5,9 +5,9 @@
<title>EduCoder</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" type="text/css" href="/stylesheets/css/edu-common.css?1525440977">
<link rel="stylesheet" type="text/css" href="/stylesheets/educoder/edu-main.css?1525440977">
<link rel="stylesheet" type="text/css" href="/stylesheets/educoder/edu-all.css?1525440977">
<link rel="stylesheet" type="text/css" href="/stylesheets/css/edu-common.css?15254409771">
<link rel="stylesheet" type="text/css" href="/stylesheets/educoder/edu-main.css?15254409771">
<link rel="stylesheet" type="text/css" href="/stylesheets/educoder/edu-all.css?15254409771">
</head>
<body style="" data-gr-c-s-loaded="true">

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

@ -0,0 +1,490 @@
/*
* jQuery File Download Plugin v1.4.5
*
* http://www.johnculviner.com
*
* Copyright (c) 2013 - John Culviner
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
*
* !!!!NOTE!!!!
* You must also write a cookie in conjunction with using this plugin in the server's response headers containing the file download:
* Set-Cookie: fileDownload=true; path=/"
* !!!!NOTE!!!!
*/
(function($, window){
// i'll just put them here to get evaluated on script load
var htmlSpecialCharsRegEx = /[<>&\r\n"']/gm;
var htmlSpecialCharsPlaceHolders = {
'<': 'lt;',
'>': 'gt;',
'&': 'amp;',
'\r': "#13;",
'\n': "#10;",
'"': 'quot;',
"'": '#39;' /*single quotes just to be safe, IE8 doesn't support &apos;, so use &#39; instead */
};
$.extend({
//
//$.fileDownload('/path/to/url/', options)
// see directly below for possible 'options'
fileDownload: function (fileUrl, options) {
//provide some reasonable defaults to any unspecified options below
var settings = $.extend({
//
//Requires jQuery UI: provide a message to display to the user when the file download is being prepared before the browser's dialog appears
//
preparingMessageHtml: null,
//
//Requires jQuery UI: provide a message to display to the user when a file download fails
//
failMessageHtml: null,
//
//the stock android browser straight up doesn't support file downloads initiated by a non GET: http://code.google.com/p/android/issues/detail?id=1780
//specify a message here to display if a user tries with an android browser
//if jQuery UI is installed this will be a dialog, otherwise it will be an alert
//Set to null to disable the message and attempt to download anyway
//
androidPostUnsupportedMessageHtml: "Unfortunately your Android browser doesn't support this type of file download. Please try again with a different browser.",
//
//Requires jQuery UI: options to pass into jQuery UI Dialog
//
dialogOptions: { modal: true },
//
//a function to call while the dowload is being prepared before the browser's dialog appears
//Args:
// url - the original url attempted
//
prepareCallback: function (url) { },
//
//a function to call after a file download successfully completed
//Args:
// url - the original url attempted
//
successCallback: function (url) { },
//
//a function to call after a file download request was canceled
//Args:
// url - the original url attempted
//
abortCallback: function (url) { },
//
//a function to call after a file download failed
//Args:
// responseHtml - the html that came back in response to the file download. this won't necessarily come back depending on the browser.
// in less than IE9 a cross domain error occurs because 500+ errors cause a cross domain issue due to IE subbing out the
// server's error message with a "helpful" IE built in message
// url - the original url attempted
// error - original error cautch from exception
//
failCallback: function (responseHtml, url, error) { },
//
// the HTTP method to use. Defaults to "GET".
//
httpMethod: "GET",
//
// if specified will perform a "httpMethod" request to the specified 'fileUrl' using the specified data.
// data must be an object (which will be $.param serialized) or already a key=value param string
//
data: null,
//
//a period in milliseconds to poll to determine if a successful file download has occured or not
//
checkInterval: 100,
//
//the cookie name to indicate if a file download has occured
//
cookieName: "fileDownload",
//
//the cookie value for the above name to indicate that a file download has occured
//
cookieValue: "true",
//
//the cookie path for above name value pair
//
cookiePath: "/",
//
//if specified it will be used when attempting to clear the above name value pair
//useful for when downloads are being served on a subdomain (e.g. downloads.example.com)
//
cookieDomain: null,
//
//the title for the popup second window as a download is processing in the case of a mobile browser
//
popupWindowTitle: "Initiating file download...",
//
//Functionality to encode HTML entities for a POST, need this if data is an object with properties whose values contains strings with quotation marks.
//HTML entity encoding is done by replacing all &,<,>,',",\r,\n characters.
//Note that some browsers will POST the string htmlentity-encoded whilst others will decode it before POSTing.
//It is recommended that on the server, htmlentity decoding is done irrespective.
//
encodeHTMLEntities: true
}, options);
var deferred = new $.Deferred();
//Setup mobile browser detection: Partial credit: http://detectmobilebrowser.com/
var userAgent = (navigator.userAgent || navigator.vendor || window.opera).toLowerCase();
var isIos; //has full support of features in iOS 4.0+, uses a new window to accomplish this.
var isAndroid; //has full support of GET features in 4.0+ by using a new window. Non-GET is completely unsupported by the browser. See above for specifying a message.
var isOtherMobileBrowser; //there is no way to reliably guess here so all other mobile devices will GET and POST to the current window.
if (/ip(ad|hone|od)/.test(userAgent)) {
isIos = true;
} else if (userAgent.indexOf('android') !== -1) {
isAndroid = true;
} else {
isOtherMobileBrowser = /avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|playbook|silk|iemobile|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(userAgent) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-/i.test(userAgent.substr(0, 4));
}
var httpMethodUpper = settings.httpMethod.toUpperCase();
if (isAndroid && httpMethodUpper !== "GET" && settings.androidPostUnsupportedMessageHtml) {
//the stock android browser straight up doesn't support file downloads initiated by non GET requests: http://code.google.com/p/android/issues/detail?id=1780
if ($().dialog) {
$("<div>").html(settings.androidPostUnsupportedMessageHtml).dialog(settings.dialogOptions);
} else {
alert(settings.androidPostUnsupportedMessageHtml);
}
return deferred.reject();
}
var $preparingDialog = null;
var internalCallbacks = {
onPrepare: function (url) {
//wire up a jquery dialog to display the preparing message if specified
if (settings.preparingMessageHtml) {
$preparingDialog = $("<div>").html(settings.preparingMessageHtml).dialog(settings.dialogOptions);
} else if (settings.prepareCallback) {
settings.prepareCallback(url);
}
},
onSuccess: function (url) {
//remove the perparing message if it was specified
if ($preparingDialog) {
$preparingDialog.dialog('close');
}
settings.successCallback(url);
deferred.resolve(url);
},
onAbort: function (url) {
//remove the perparing message if it was specified
if ($preparingDialog) {
$preparingDialog.dialog('close');
};
settings.abortCallback(url);
deferred.reject(url);
},
onFail: function (responseHtml, url, error) {
//remove the perparing message if it was specified
if ($preparingDialog) {
$preparingDialog.dialog('close');
}
//wire up a jquery dialog to display the fail message if specified
if (settings.failMessageHtml) {
$("<div>").html(settings.failMessageHtml).dialog(settings.dialogOptions);
}
settings.failCallback(responseHtml, url, error);
deferred.reject(responseHtml, url);
}
};
internalCallbacks.onPrepare(fileUrl);
//make settings.data a param string if it exists and isn't already
if (settings.data !== null && typeof settings.data !== "string") {
settings.data = $.param(settings.data);
}
var $iframe,
downloadWindow,
formDoc,
$form;
if (httpMethodUpper === "GET") {
if (settings.data !== null) {
//need to merge any fileUrl params with the data object
var qsStart = fileUrl.indexOf('?');
if (qsStart !== -1) {
//we have a querystring in the url
if (fileUrl.substring(fileUrl.length - 1) !== "&") {
fileUrl = fileUrl + "&";
}
} else {
fileUrl = fileUrl + "?";
}
fileUrl = fileUrl + settings.data;
}
if (isIos || isAndroid) {
downloadWindow = window.open(fileUrl);
downloadWindow.document.title = settings.popupWindowTitle;
window.focus();
} else if (isOtherMobileBrowser) {
window.location(fileUrl);
} else {
//create a temporary iframe that is used to request the fileUrl as a GET request
$iframe = $("<iframe style='display: none' src='"+fileUrl+"'></iframe>").appendTo("body");
}
} else {
var formInnerHtml = "";
if (settings.data !== null) {
$.each(settings.data.replace(/\+/g, ' ').split("&"), function () {
var kvp = this.split("=");
//Issue: When value contains sign '=' then the kvp array does have more than 2 items. We have to join value back
var k = kvp[0];
kvp.shift();
var v = kvp.join("=");
kvp = [k, v];
var key = settings.encodeHTMLEntities ? htmlSpecialCharsEntityEncode(decodeURIComponent(kvp[0])) : decodeURIComponent(kvp[0]);
if (key) {
var value = settings.encodeHTMLEntities ? htmlSpecialCharsEntityEncode(decodeURIComponent(kvp[1])) : decodeURIComponent(kvp[1]);
formInnerHtml += '<input type="hidden" name="' + key + '" value="' + value + '" />';
}
});
}
if (isOtherMobileBrowser) {
$form = $("<form>").appendTo("body");
$form.hide()
.prop('method', settings.httpMethod)
.prop('action', fileUrl)
.html(formInnerHtml);
} else {
if (isIos) {
downloadWindow = window.open("about:blank");
downloadWindow.document.title = settings.popupWindowTitle;
formDoc = downloadWindow.document;
window.focus();
} else {
$iframe = $("<iframe style='display: none' src='about:blank'></iframe>").appendTo("body");
formDoc = getiframeDocument($iframe);
}
formDoc.write("<html><head></head><body><form method='" + settings.httpMethod + "' action='" + fileUrl + "'>" + formInnerHtml + "</form>" + settings.popupWindowTitle + "</body></html>");
$form = $(formDoc).find('form');
}
$form.submit();
}
//check if the file download has completed every checkInterval ms
setTimeout(checkFileDownloadComplete, settings.checkInterval);
function checkFileDownloadComplete() {
//has the cookie been written due to a file download occuring?
var cookieValue = settings.cookieValue;
if(typeof cookieValue == 'string') {
cookieValue = cookieValue.toLowerCase();
}
var lowerCaseCookie = settings.cookieName.toLowerCase() + "=" + cookieValue;
if (document.cookie.toLowerCase().indexOf(lowerCaseCookie) > -1) {
//execute specified callback
internalCallbacks.onSuccess(fileUrl);
//remove cookie
var cookieData = settings.cookieName + "=; path=" + settings.cookiePath + "; expires=" + new Date(0).toUTCString() + ";";
if (settings.cookieDomain) cookieData += " domain=" + settings.cookieDomain + ";";
document.cookie = cookieData;
//remove iframe
cleanUp(false);
return;
}
//has an error occured?
//if neither containers exist below then the file download is occuring on the current window
if (downloadWindow || $iframe) {
//has an error occured?
try {
var formDoc = downloadWindow ? downloadWindow.document : getiframeDocument($iframe);
if (formDoc && formDoc.body !== null && formDoc.body.innerHTML.length) {
var isFailure = true;
if ($form && $form.length) {
var $contents = $(formDoc.body).contents().first();
try {
if ($contents.length && $contents[0] === $form[0]) {
isFailure = false;
}
} catch (e) {
if (e && e.number == -2146828218) {
// IE 8-10 throw a permission denied after the form reloads on the "$contents[0] === $form[0]" comparison
isFailure = true;
} else {
throw e;
}
}
}
if (isFailure) {
// IE 8-10 don't always have the full content available right away, they need a litle bit to finish
setTimeout(function () {
internalCallbacks.onFail(formDoc.body.innerHTML, fileUrl);
cleanUp(true);
}, 100);
return;
}
}
}
catch (err) {
//500 error less than IE9
internalCallbacks.onFail('', fileUrl, err);
cleanUp(true);
return;
}
}
//keep checking...
setTimeout(checkFileDownloadComplete, settings.checkInterval);
}
//gets an iframes document in a cross browser compatible manner
function getiframeDocument($iframe) {
var iframeDoc = $iframe[0].contentWindow || $iframe[0].contentDocument;
if (iframeDoc.document) {
iframeDoc = iframeDoc.document;
}
return iframeDoc;
}
function cleanUp(isFailure) {
setTimeout(function() {
if (downloadWindow) {
if (isAndroid) {
downloadWindow.close();
}
if (isIos) {
if (downloadWindow.focus) {
downloadWindow.focus(); //ios safari bug doesn't allow a window to be closed unless it is focused
if (isFailure) {
downloadWindow.close();
}
}
}
}
//iframe cleanup appears to randomly cause the download to fail
//not doing it seems better than failure...
//if ($iframe) {
// $iframe.remove();
//}
}, 0);
}
function htmlSpecialCharsEntityEncode(str) {
return str.replace(htmlSpecialCharsRegEx, function(match) {
return '&' + htmlSpecialCharsPlaceHolders[match];
});
}
var promise = deferred.promise();
promise.abort = function() {
cleanUp();
$iframe.attr('src', '').html('');
internalCallbacks.onAbort(fileUrl);
};
return promise;
}
});
})(jQuery, this || window);

File diff suppressed because one or more lines are too long

@ -1,11 +1,25 @@
/*--------------------------首页*/
/*头部导航条样式---2018-03-19--by-cs*/
.newHeader{background: #24292D !important; width:100%; height: 60px !important; min-width: 1200px;position: fixed;top: 0px;left: 0px;z-index:1000;-moz-box-shadow: 0px 0px 12px rgba(0,0,0,0.1); /* 老的 Firefox */box-shadow: 0px 0px 12px rgba(0,0,0,0.1);}
.newHeader{
/*overflow:hidden;*/
/*text-overflow:ellipsis;*/
/*white-space:nowrap;*/
background: #24292D !important; width:100%; height: 60px !important; min-width: 1200px;position: fixed;top: 0px;left: 0px;z-index:1000;-moz-box-shadow: 0px 0px 12px rgba(0,0,0,0.1); /* 老的 Firefox */box-shadow: 0px 0px 12px rgba(0,0,0,0.1);
}
.newHeader .logoimg{
margin-top: 16px;
float: left;
width: 97px;}
.head-nav{float: left;width: 830px;text-align: center;height: 60px;box-sizing: border-box; min-width: 400px;}
.head-nav{
float: left;
text-align: center;
height: 60px;
box-sizing: border-box;
min-width: 785px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.head-nav ul#header-nav{position: absolute;top: 0px;z-index: 3;height: 60px;box-sizing: border-box;}
.head-nav ul#header-nav li{float: left;height: 60px;line-height: 60px;margin-right: 30px;cursor: pointer;position: relative;font-size: 16px}
.head-nav ul#header-nav li a{display: block;height: 100%;width: 100%;color: #fff}
@ -406,7 +420,7 @@ table.text-file{}
/*-------------------------------实训路径-------------------------------*/
.path-head{width: 100%;height: 300px;background-image: url("/images/educoder/path.jpg");background-color: #081C4B;background-size: 100% 100%;}
.path-head{width: 100%;height: 300px;background-image: url("/images/educoder/path.png");background-color: #081C4B;background-size: 100% 100%;}
.pathNavLine{position: absolute;bottom: -8px;width: 100%;}
.path-nav li{float: left;padding: 0px 30px;height: 42px;}
.path-nav li a{color:#fff;font-size: 16px;display: block; height: 40px;}

@ -87,7 +87,7 @@
</noscript>
<!--用于markdown转html -->
<div id="md_div" style="display: none;"></div>
<div id="root" class="page -layout-v -fit" >
<div id="root" class="page -layout-v -fit widthunit">
<!--<div class="d2-home">-->
<!--<div class="d2-home__main">-->
<!--&lt;!&ndash;<img class="d2-home__loading"&ndash;&gt;-->

@ -226,6 +226,12 @@ const Interestpage = Loadable({
loading: Loading,
})
//众包创新
const ProjectPackages=Loadable({
loader: () => import('./modules/projectPackages/ProjectPackageIndex'),
loading: Loading,
})
class App extends Component {
constructor(props) {
super(props)
@ -296,6 +302,9 @@ class App extends Component {
<Router>
<Switch>
{/*<Route path="/login" component={LoginRegisterPage}/>*/}
{/*众包创新*/}
<Route path={"/crowdsourcings"} component={ProjectPackages}/>
{/*认证*/}
<Route path="/account" component={AccountPage}/>

@ -1,4 +1,6 @@
import { bytesToSize } from 'educoder';
import { bytesToSize, getUrl2 } from 'educoder';
const $ = window.$
export function isImageExtension(fileName) {
return fileName ? !!(fileName.match(/.(jpg|jpeg|png|gif)$/i)) : false
}
@ -10,7 +12,8 @@ export function markdownToHTML(oldContent, selector) {
window.$(selector).html(oldContent)
} else {
try {
var markdwonParser = window.editormd.markdownToHTML("md_div", {
// selector ||
var markdwonParser = window.editormd.markdownToHTML(selector || "md_div", {
markdown: oldContent, // .replace(/▁/g,"▁▁▁"),
emoji: true,
htmlDecode: "style,script,iframe", // you can filter tags decode
@ -23,6 +26,10 @@ export function markdownToHTML(oldContent, selector) {
} catch(e) {
console.error(e)
}
// selector = '.' + selector
if (selector) {
return;
}
const content = window.$('#md_div').html()
if (selector) {
@ -31,6 +38,25 @@ export function markdownToHTML(oldContent, selector) {
return content
}
}
function _doDownload(options) {
$.fileDownload("/api" + options.url, {
successCallback: options.successCallback,
failCallback: options.failCallback
});
}
export function downloadFile(options) {
if ($.fileDownload) {
_doDownload(options)
} else {
const _url_origin = getUrl2()
$.getScript(
`${_url_origin}/javascripts/download/jquery.fileDownload.min.js`,
(data, textStatus, jqxhr) => {
_doDownload(options)
});
}
}
export function appendFileSizeToUploadFile(item) {
return `${item.title}${uploadNameSizeSeperator}${item.filesize}`

@ -0,0 +1,6 @@
.markdownToHtml.editormd-html-preview, .markdownToHtml.editormd-preview-container {
overflow: hidden;
}
.markdownToHtml.editormd-html-preview p.editormd-tex, .markdownToHtml.editormd-preview-container p.editormd-tex {
text-align: left;
}

@ -1,8 +1,8 @@
import React,{ Component } from "react";
import { markdownToHTML } from 'educoder'
import './MarkdownToHtml.css'
/**
selector 需要传入唯一的selector作为id不然会引起冲突
delay 如果有公式需要传入delay={true}
*/
class MarkdownToHtml extends Component{
constructor(props){
@ -10,33 +10,26 @@ class MarkdownToHtml extends Component{
this.state={
}
}
_markdownToHTML = (content, selector) => {
if (this.props.delay == true) {
(function(content, selector) {
// console.log('selector: ', selector)
setTimeout(() => {
markdownToHTML(content, selector)
}, 600)
})(content, selector)
} else {
markdownToHTML(content, selector)
}
_markdownToHTML = (content, selector) => {
markdownToHTML(content, selector)
}
componentDidUpdate = (prevProps) => {
if (this.props.content) {
if ( prevProps.content != this.props.content ) {
this._markdownToHTML(this.props.content, `.markdown_to_html_${this.props.selector || ''}`)
this._markdownToHTML(this.props.content, `markdown_to_html_${this.props.selector || ''}`)
}
}
}
componentDidMount () {
this.props.content && this._markdownToHTML(this.props.content, `.markdown_to_html_${this.props.selector || ''}`)
this.props.content && this._markdownToHTML(this.props.content, `markdown_to_html_${this.props.selector || ''}`)
}
render(){
const { style, className } = this.props
let _selector = `markdown_to_html_${this.props.selector || ''}`
return(
<div id="memo_content_editorMd" className={`new_li markdown-body ${className} markdown_to_html_${this.props.selector || ''}`}
<div id={_selector } className={`markdownToHtml new_li markdown-body ${className} ${_selector}`}
// dangerouslySetInnerHTML={{__html: markdownToHTML(this.props.content)}}
style={style}
>

@ -15,7 +15,8 @@ export { updatePageParams as updatePageParams } from './RouterUti
export { bytesToSize as bytesToSize } from './UnitUtil';
export { markdownToHTML, uploadNameSizeSeperator, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll, isImageExtension } from './TextUtil'
export { markdownToHTML, uploadNameSizeSeperator, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll, isImageExtension,
downloadFile } from './TextUtil'
export { handleDateString, getNextHalfHourOfMoment,formatDuring } from './DateUtil'

@ -103,7 +103,7 @@ class ListPageIndex extends Component{
<div className="newMain clearfix">
{/*头部banner*/}
<CoursesBanner {...this.props}></CoursesBanner>
{/*{yslGuideone===null||yslGuideone===undefined||yslGuideone===false?*/}
{/* (*/}
{/* this.props.isAdmin()===true?*/}

@ -8,7 +8,7 @@ import { CNotificationHOC } from '../common/CNotificationHOC'
import { RouteHOC } from './common'
import locale from 'antd/lib/date-picker/locale/zh_CN';
import { WordsBtn, MarkdownToHtml, trigger, queryString } from 'educoder';
import { WordsBtn, MarkdownToHtml, trigger, queryString, downloadFile } from 'educoder';
import axios from 'axios';
import Modals from '../../modals/Modals';
import CoursesListType from '../coursesPublic/CoursesListType';
@ -18,6 +18,8 @@ import '../css/Courses.css'
import CBreadcrumb from '../common/CBreadcrumb'
import DownloadMessageysl from "../../modals/DownloadMessageysl";
import { Spin } from 'antd'
//引入对应跳转的组件
//新建分组/普通作业
@ -64,6 +66,7 @@ class CommonWorkDetailIndex extends Component{
this.state = {
DownloadType:false,
DownloadMessageval:undefined,
donwloading:false,
}
}
initWorkDetailCommonState = (data) => {
@ -103,7 +106,10 @@ class CommonWorkDetailIndex extends Component{
/// 确认是否下载
confirmysl(url){
axios.get(url).then((response) => {
axios.get(url+ '&export=true').then((response) => {
if(response===undefined){
return
}
if(response.data.status&&response.data.status===-1){
}else if(response.data.status&&response.data.status===-2){
@ -123,7 +129,20 @@ class CommonWorkDetailIndex extends Component{
}
}else {
this.props.showNotification(`正在下载中`);
window.open("/api"+url, '_blank');
this.setState({ donwloading: true })
downloadFile({
url: url,
successCallback: (url) => {
this.setState({ donwloading: false })
console.log('successCallback')
},
failCallback: (responseHtml, url) => {
this.setState({ donwloading: false })
console.log('failCallback')
}
})
// window.open("/api"+url, '_blank');
}
}).catch((error) => {
console.log(error)
@ -136,7 +155,7 @@ class CommonWorkDetailIndex extends Component{
DownloadMessageval:undefined
})
}
bindRef = ref => { this.child = ref };
render() {
@ -171,8 +190,12 @@ class CommonWorkDetailIndex extends Component{
let params = {}
if (isListModule) {
// TODO
// params = this.refs.commonWorkList._getRequestParams()
if(this.child!=undefined) {
params = this.child._getRequestParams() !== undefined ? this.child._getRequestParams() : {};
}
}
// console.log("普通作业176176176");
// console.log(params);
let exportUrl = `/homework_commons/${workId}/works_list.zip?${queryString.stringify(params)}`
let exportResultUrl = `/homework_commons/${workId}/works_list.xlsx?${queryString.stringify(params)}`
return (
@ -244,7 +267,7 @@ class CommonWorkDetailIndex extends Component{
}
</div>
{ noTab !== true && <div className="stud-class-set bor-bottom-greyE">
{ noTab !== true && <div className="stud-class-set bor-bottom-greyE floatSpinParent">
<div className="mt10 clearfix edu-back-white poll_list pl5">
<Link
onClick={() => this.setState({moduleName: '作品列表'})}
@ -283,18 +306,30 @@ class CommonWorkDetailIndex extends Component{
padding-top: 10px;
padding-bottom: 8px;
}
.floatSpinParent .ant-spin-nested-loading {
float: right;
}
`}</style>
{this.props.isAdmin()? <li className="li_line drop_down fr color-blue font-16 mr8 mt20" style={{"padding":"0 20px"}}>
{this.props.isAdmin()? <Spin spinning={this.state.donwloading} style={{ }}>
<li className="li_line drop_down fr color-blue font-16 mr8 mt20" style={{"padding":"0 20px"}}>
导出<i className="iconfont icon-xiajiantou font-12 ml2"></i>
<ul className="drop_down_menu" style={{"right":"-34px","left":"unset","height":"auto"}}>
<li><a href={"javascript:void(0)"} className="color-dark"
onClick={() => this.confirmysl(exportResultUrl, exportParams)}
>导出成绩</a></li>
<li><a href={"javascript:void(0)"} className="color-dark"
onClick={() => this.confirmysl(exportUrl, exportParams)}
>导出作品附件</a></li>
<li>
<a href={"javascript:void(0)"} className="color-dark"
onClick={() => this.confirmysl(exportResultUrl)}
>导出成绩</a>
</li>
<li>
<a href={"javascript:void(0)"} className="color-dark"
onClick={() => this.confirmysl(exportUrl)}
>导出作品附件</a>
</li>
</ul>
</li>:""}
</li>
</Spin>:""}
{/* {isAdmin && <a className={"fr color-blue font-16"} href={exportUrl}></a>}
{isAdmin && <a className={"fr color-blue font-16"} href={exportResultUrl}>导出成绩</a>} */}
@ -356,7 +391,7 @@ class CommonWorkDetailIndex extends Component{
{/* 作品列表 */}
<Route exact path="/courses/:coursesId/common_homeworks/:workId/list"
render={
(props) => (<CommonWorkList ref="commonWorkList" {...this.props} {...props} {...this.state} {...commonHandler}/>)
(props) => (<CommonWorkList ref="commonWorkList" triggerRef={this.bindRef} {...this.props} {...props} {...this.state} {...commonHandler}/>)
}
></Route>
@ -382,7 +417,7 @@ class CommonWorkDetailIndex extends Component{
{/* 作品列表 */}
<Route exact path="/courses/:coursesId/group_homeworks/:workId/list"
render={
(props) => (<CommonWorkList {...this.props} {...props} {...this.state} {...commonHandler}/>)
(props) => (<CommonWorkList triggerRef={this.bindRef} {...this.props} {...props} {...this.state} {...commonHandler}/>)
}
></Route>

@ -400,7 +400,12 @@ class CommonWorkList extends Component{
componentDidMount() {
this.fetchList()
on('commonwork_fetch_all', this.fetchAllListener)
$("html").animate({ scrollTop: $('html').scrollTop() - 100 })
$("html").animate({ scrollTop: $('html').scrollTop() - 100 });
try {
this.props.triggerRef(this);
}catch (e) {
}
}
componentWillUnmount() {
@ -420,7 +425,8 @@ class CommonWorkList extends Component{
teacher_comment: arg_teacher_comment.length == 0 ? '' : arg_teacher_comment[0],
order,
limit: PAGE_SIZE,
b_order: orderMap[order]
b_order: orderMap[order],
group_id:arg_course_group,
}
}
fetchList = () => {
@ -767,7 +773,7 @@ class CommonWorkList extends Component{
<div className="mh650 edu-back-white">
<div className="edu-tab-con-box clearfix edu-txt-center">
<img className="edu-nodata-img mb20" src={getImageUrl("images/educoder/nodata.png")}/>
<p className="edu-nodata-p mb30">没有数据可以显示</p>
<p className="edu-nodata-p mb30">暂时还没有相关数据哦</p>
</div>
</div>
</div>

@ -44,7 +44,7 @@ class TabRightComponents extends Component{
}
/// 确认是否下载
confirmysl(url){
axios.get(url).then((response) => {
axios.get(url + '?export=true' ).then((response) => {
if(response.data.status&&response.data.status===-1){
}else if(response.data.status&&response.data.status===-2){

@ -33,6 +33,21 @@ class ExerciseDisplay extends Component{
this.state = {
exercise_questions: [],
exercise_group_id:[],
page:1,
limit:10,
searchtext:"",
order: "end_at",
}
}
_getRequestParams() {
const { order, exercise_group_id,searchtext, page ,limit} = this.state
return {
page,
search:searchtext,
order,
limit: limit,
group_id:exercise_group_id,
}
}
componentDidMount = () => {
@ -49,6 +64,21 @@ class ExerciseDisplay extends Component{
console.log(error);
});
}
try {
this.props.triggerRef(this);
}catch (e) {
}
}
_getRequestParams() {
const { order, exercise_group_id,searchtext, page ,limit} = this.state
return {
page,
search:searchtext,
order,
limit: limit,
group_id:exercise_group_id,
}
}
render() {
// let { question_title, question_score, question_type, question_choices, standard_answer,

@ -7,7 +7,7 @@ import '../poll/pollStyle.css'
import '../css/Courses.css'
import moment from 'moment'
import { WordsBtn,markdownToHTML,ActionBtn,getImageUrl } from 'educoder'
import { WordsBtn,markdownToHTML,ActionBtn,getImageUrl, MarkdownToHtml } from 'educoder'
import Modals from '../../modals/Modals'
import CoursesListType from '../coursesPublic/CoursesListType';
@ -537,7 +537,7 @@ class ExerciseReviewAndAnswer extends Component{
/>
<div className="educontent mt10 mb50">
<p className="clearfix mb20">
<WordsBtn style="grey" className="fl" to={current_user.first_category_url}>{courseName}</WordsBtn>
<WordsBtn style="grey" className="fl" to={current_user && current_user.first_category_url}>{courseName}</WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<WordsBtn style="grey" className="fl" to={`/courses/${coursesId}/exercises/${data && data.left_banner_id}`}>{data && data.left_banner_name}</WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
@ -712,7 +712,10 @@ class ExerciseReviewAndAnswer extends Component{
</span>
</p>
<li className="break_word mt15 mb15 pl30 pr30">
<p className="standardAnswer markdown-body" dangerouslySetInnerHTML={{__html: markdownToHTML(item.question_type == 5 ? item.shixun_name : item.question_title).replace(/▁/g,"▁▁▁")}}></p>
{/* <p className="standardAnswer markdown-body" dangerouslySetInnerHTML={{__html: markdownToHTML1(item.question_type == 5 ? item.shixun_name : item.question_title).replace(/▁/g,"▁▁▁")}}></p> */}
<MarkdownToHtml content={(item.question_type == 5 ? item.shixun_name : item.question_title)} selector={'answer_' + key}
className="standardAnswer"
></MarkdownToHtml>
</li>
{
// 选择题和判断题共用
@ -724,6 +727,7 @@ class ExerciseReviewAndAnswer extends Component{
questionType={item}
user_exercise_status={user_exercise_status}
changeQuestionStatus={(No,flag)=>this.changeQuestionStatus(No,flag)}
index={key}
></Single>
}
{
@ -736,6 +740,8 @@ class ExerciseReviewAndAnswer extends Component{
questionType={item}
user_exercise_status={user_exercise_status}
changeQuestionStatus={(No,flag)=>this.changeQuestionStatus(No,flag)}
index={key}
></Multiple>
}
{
@ -748,6 +754,8 @@ class ExerciseReviewAndAnswer extends Component{
questionType={item}
user_exercise_status={user_exercise_status}
changeQuestionStatus={(No,flag)=>this.changeQuestionStatus(No,flag)}
index={key}
></FillEmpty>
}
{
@ -774,6 +782,8 @@ class ExerciseReviewAndAnswer extends Component{
questionType={item}
user_exercise_status={user_exercise_status}
id={this.state.Id}
index={key}
></ShixunAnswer>
}

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

Loading…
Cancel
Save