dev_cs_new
杨树明 6 years ago
commit ed19add0af

@ -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.

@ -160,7 +160,7 @@ class AccountsController < ApplicationController
# 发送验证码 # 发送验证码
# params[:login] 手机号或者邮箱号 # params[:login] 手机号或者邮箱号
# params[:type]为事件通知类型 1用户注册注册 2忘记密码 3: 绑定手机 4: 绑定邮箱 # 如果有新的继续后面加 # params[:type]为事件通知类型 1用户注册注册 2忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验收手机号有效 # 如果有新的继续后面加
# 发送验证码send_type 1注册手机验证码 2找回密码手机验证码 3找回密码邮箱验证码 4绑定手机 5绑定邮箱 # 发送验证码send_type 1注册手机验证码 2找回密码手机验证码 3找回密码邮箱验证码 4绑定手机 5绑定邮箱
# 6手机验证码登录 7邮箱验证码登录 8邮箱注册验证码 9: 验收手机号有效 # 6手机验证码登录 7邮箱验证码登录 8邮箱注册验证码 9: 验收手机号有效
def get_verification_code def get_verification_code
@ -200,7 +200,7 @@ class AccountsController < ApplicationController
session[:user_id] = nil session[:user_id] = nil
end end
# type 事件类型 1用户注册 2忘记密码 3: 绑定手机 4: 绑定邮箱 # 如果有新的继续后面加 # type 事件类型 1用户注册 2忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验证手机号是否有效 # 如果有新的继续后面加
# login_type 1手机类型 2邮箱类型 # login_type 1手机类型 2邮箱类型
def verify_type login_type, type def verify_type login_type, type
case type case type
@ -212,6 +212,8 @@ class AccountsController < ApplicationController
login_type == 1 ? 4 : tip_exception('请填写正确的手机号') login_type == 1 ? 4 : tip_exception('请填写正确的手机号')
when 4 when 4
login_type == 1 ? tip_exception('请填写正确的邮箱') : 5 login_type == 1 ? tip_exception('请填写正确的邮箱') : 5
when 5
login_type == 1 ? 9 : tip_exception('请填写正确的手机号')
end end
end end

@ -38,6 +38,7 @@ class ApplicationController < ActionController::Base
def user_course_identity def user_course_identity
@user_course_identity = current_user.course_identity(@course) @user_course_identity = current_user.course_identity(@course)
if @user_course_identity > Course::STUDENT && @course.is_public == 0 if @user_course_identity > Course::STUDENT && @course.is_public == 0
tip_exception(401, "..") unless User.current.logged?
tip_exception(409, "您没有权限进入") tip_exception(409, "您没有权限进入")
end end
uid_logger("###############user_course_identity:#{@user_course_identity}") uid_logger("###############user_course_identity:#{@user_course_identity}")
@ -63,10 +64,10 @@ class ApplicationController < ActionController::Base
# 发送及记录激活码 # 发送及记录激活码
# 发送验证码type 1注册手机验证码 2找回密码手机验证码 3找回密码邮箱验证码 4绑定手机 5绑定邮箱 # 发送验证码type 1注册手机验证码 2找回密码手机验证码 3找回密码邮箱验证码 4绑定手机 5绑定邮箱
# 6手机验证码登录 7邮箱验证码登录 8邮箱注册验证码 # 6手机验证码登录 7邮箱验证码登录 8邮箱注册验证码 9验证手机号有效
def check_verification_code(code, send_type, value) def check_verification_code(code, send_type, value)
case send_type case send_type
when 1, 2, 4 when 1, 2, 4, 9
# 手机类型的发送 # 手机类型的发送
sigle_para = {phone: value} sigle_para = {phone: value}
status = Educoder::Sms.send(mobile: value, code: code) status = Educoder::Sms.send(mobile: value, code: code)
@ -240,6 +241,9 @@ class ApplicationController < ActionController::Base
User.current = User.find 57703 User.current = User.find 57703
end end
# 测试版前端需求
if request.host == "47.96.87.25"
if params[:debug] == 'teacher' #todo 为了测试,记得讲debug删除 if params[:debug] == 'teacher' #todo 为了测试,记得讲debug删除
User.current = User.find 81403 User.current = User.find 81403
elsif params[:debug] == 'student' elsif params[:debug] == 'student'
@ -247,7 +251,7 @@ class ApplicationController < ActionController::Base
elsif params[:debug] == 'admin' elsif params[:debug] == 'admin'
User.current = User.find 1 User.current = User.find 1
end end
end
# User.current = User.find 81403 # User.current = User.find 81403
end end
@ -582,4 +586,8 @@ class ApplicationController < ActionController::Base
def render_parameter_missing def render_parameter_missing
render json: { status: -1, message: '参数缺失' } render json: { status: -1, message: '参数缺失' }
end end
def set_export_cookies
cookies[:fileDownload] = true
end
end end

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

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

@ -1,7 +1,7 @@
class DiscussesController < ApplicationController class DiscussesController < ApplicationController
LIMIT = 10 LIMIT = 10
before_action :find_container, only: [:index, :hidden] before_action :find_container, only: [:index, :hidden]
before_action :find_discuss, except: [:create, :index, :new_message, :reward_code] before_action :find_discuss, except: [:create, :index, :new_message, :reward_code, :forum_discusses]
def index def index
page = params[:page].to_i page = params[:page].to_i
@ -28,6 +28,48 @@ class DiscussesController < ApplicationController
@current_user = current_user @current_user = current_user
end end
def forum_discusses
page = params[:page] || 1
limit = params[:limit] || 15
offset = (page.to_i-1) * limit
search = params[:search]
tag = params[:tag_repertoire_id]
sql, sql1, sql2 = '', '', ''
sql1 =
unless search.blank?
"and d.content like '%#{search}%'"
end
sql2 =
if tag
shixun_ids = ShixunTagRepertoire.where(:tag_repertoire_id => tag).pluck(:shixun_id)
"and d.dis_id in(#{shixun_ids.join(",")})"
end
sql = "select d.id from discusses d join shixuns s on d.dis_id = s.id where s.status = 2 and s.hidden = false and d.root_id is null
and d.hidden = false #{sql1} #{sql2} order by d.created_at desc"
memo_ids = Discuss.find_by_sql(sql).pluck(:id)
@memo_count = memo_ids.size
memo_ids = memo_ids[offset, limit]
order_ids = memo_ids.size > 0 ? memo_ids.join(',') : -1
@memos = Discuss.where(id: memo_ids).order("field(id,#{order_ids})").includes(:praise_treads, dis: :tag_repertoires, user: :user_extension)
# @memos = memos.includes(:praise_treads, user: :user_extension).page(page).per(limit)
# 实训标签使用最多的9个
# @hot_tags = TagRepertoire.find_by_sql("select distinct(a.name), a.id from
# (select tr.id, tr.name, count(d.dis_id) cnt
# from tag_repertoires tr join (shixun_tag_repertoires str
# left join (shixuns s join discusses d on d.dis_id = s.id)
# on s.id = str.shixun_id) on tr.id = str.tag_repertoire_id
# group by d.dis_id order by cnt desc) a limit 9").map{|ht| ht.attributes.dup}
tag_id = ShixunTagRepertoire.joins(:shixun).order("myshixuns_count desc").pluck(:tag_repertoire_id).uniq.first(9)
@hot_tags = TagRepertoire.select([:id, :name]).where(id: tag_id).order("FIELD(id, #{tag_id.join(",")})").map{|ht| ht.attributes.dup} if tag_id
@memos = DiscussesService.new.memo_list @memos
@hot_memos = Memo.field_for_recommend.posts.hot.includes(:tag_repertoires).limit(4)
@recommend_shixuns = DiscussesService.new.recommends
end
def new_message def new_message
onclick_time = Myshixun.find(params[:myshixun_id]).try(:onclick_time) onclick_time = Myshixun.find(params[:myshixun_id]).try(:onclick_time)
ids = Discuss.where(user_id: User.current.id, dis_id: params[:container_id], dis_type: params[:container_type]). ids = Discuss.where(user_id: User.current.id, dis_id: params[:container_id], dis_type: params[:container_type]).

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

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

@ -132,12 +132,14 @@ class GraduationTasksController < ApplicationController
tip_exception(403, "无权限操作") tip_exception(403, "无权限操作")
elsif complete_works == 0 elsif complete_works == 0
normal_status(-1,"暂无用户提交") normal_status(-1,"暂无用户提交")
elsif params[:export].present? && params[:export]
normal_status(0,"正在下载中")
else else
respond_to do |format| respond_to do |format|
format.xlsx{ format.xlsx{
graduation_work_to_xlsx(@work_excel,@task,current_user) 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')}" 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
end end
@ -148,13 +150,18 @@ class GraduationTasksController < ApplicationController
zip_works = @work_excel.where("work_status > 0") zip_works = @work_excel.where("work_status > 0")
status = checkfileSize(zip_works) status = checkfileSize(zip_works)
if status == 0 if status == 0
if params[:export].present? && params[:export]
normal_status(0,"正在下载中")
else
respond_to do |format| respond_to do |format|
format.zip{ format.zip{
set_export_cookies
zipfile = zip_homework_common @task, zip_works zipfile = zip_homework_common @task, zip_works
file = decode64(zipfile[0][:base64file]) file = decode64(zipfile[0][:base64file])
send_file "#{OUTPUT_FOLDER}/#{file}", filename: filename_for_content_disposition(file), type: 'application/zip' send_file "#{OUTPUT_FOLDER}/#{file}", filename: filename_for_content_disposition(file), type: 'application/zip'
} }
end end
end
else else
normal_status(status,status == -2 ? "500M" : "无附件可下载") normal_status(status,status == -2 ? "500M" : "无附件可下载")
end end

@ -271,7 +271,12 @@ class GraduationTopicsController < ApplicationController
students = course.students.joins(user: :user_extension).order("user_extensions.student_id") students = course.students.joins(user: :user_extension).order("user_extensions.student_id")
graduation_topic_to_xlsx(students,course) graduation_topic_to_xlsx(students,course)
topic_export_name_ = "#{current_user.real_name}_#{course.name}_毕设选题_#{Time.now.strftime('%Y%m%d_%H%M%S')}" 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 rescue Exception => e
uid_logger(e.message) uid_logger(e.message)
missing_template missing_template

@ -207,9 +207,12 @@ class HomeworkCommonsController < ApplicationController
tip_exception(403, "无权限操作") tip_exception(403, "无权限操作")
elsif @work_excel.blank? || @work_excel.size == 0 elsif @work_excel.blank? || @work_excel.size == 0
normal_status(-1,"暂无用户提交!") normal_status(-1,"暂无用户提交!")
elsif params[:export].present? && params[:export]
normal_status(0,"正在下载中")
else else
respond_to do |format| respond_to do |format|
format.xlsx{ format.xlsx{
set_export_cookies
student_work_to_xlsx(@work_excel,@homework) 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')}" 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}",template: "homework_commons/works_list.xlsx.axlsx",locals: render xlsx: "#{exercise_export_name.strip}",template: "homework_commons/works_list.xlsx.axlsx",locals:
@ -229,13 +232,18 @@ class HomeworkCommonsController < ApplicationController
end end
if status == 0 if status == 0
if params[:export].present? && params[:export]
normal_status(0,"正在下载中")
else
respond_to do |format| respond_to do |format|
format.zip{ format.zip{
set_export_cookies
zipfile = zip_homework_common @homework, zip_works zipfile = zip_homework_common @homework, zip_works
file = decode64(zipfile[0][:base64file]) file = decode64(zipfile[0][:base64file])
send_file "#{OUTPUT_FOLDER}/#{file}", filename: filename_for_content_disposition(file), type: 'application/zip' send_file "#{OUTPUT_FOLDER}/#{file}", filename: filename_for_content_disposition(file), type: 'application/zip'
} }
end end
end
else else
normal_status(status, status == -2 ? "500M" : "无附件可下载") normal_status(status, status == -2 ? "500M" : "无附件可下载")
end end
@ -328,7 +336,7 @@ class HomeworkCommonsController < ApplicationController
@messages = @messages.parent_comment @messages = @messages.parent_comment
end end
@messages = @messages.page(@page).per(@limit).order("created_on desc") @messages = @messages.includes(:praise_treads).page(@page).per(@limit).order("created_on desc")
end end
def reference_answer def reference_answer

@ -1,5 +1,9 @@
class MemosController < ApplicationController class MemosController < ApplicationController
before_action :set_memo, only: [:show, :edit, :update, :destroy] before_action :require_login, except: [:show, :index]
before_action :set_memo, only: [:show, :edit, :update, :destroy, :sticky_or_cancel, :hidden, :more_reply]
before_action :validate_memo_params, only: [:create, :update]
before_action :owner_or_admin, only: [:edit, :update, :destroy]
before_action :is_admin, only: [:sticky_or_cancel, :hidden]
include ApplicationHelper include ApplicationHelper
# GET /memos # GET /memos
@ -9,25 +13,19 @@ class MemosController < ApplicationController
@memos = Memo.all @memos = Memo.all
s_order = (params[:order] == "replies_count" ? "all_replies_count" : params[:order]) || "updated_at" s_order = (params[:order] == "replies_count" ? "all_replies_count" : params[:order]) || "updated_at"
# @tidding_count = unviewed_tiddings(current_user) if current_user.present? # @tidding_count = unviewed_tiddings(current_user) if current_user.present?
page = params[:page].to_i page = params[:page] || 1
limit = params[:limit] || 15
search = params[:search] search = params[:search]
offset = page * 15
forum_id = params[:forum] forum_id = params[:forum]
user_id = params[:user_id]
if user_id == -1
user_id = current_user.try(:id)
end
tag_repertoire_id = params[:tag_repertoire_id] tag_repertoire_id = params[:tag_repertoire_id]
sql = sql =
if forum_id if forum_id
search ? "forum_id = #{forum_id} and root_id is null and subject like '%#{search}%'" : !search.blank? ? "forum_id = #{forum_id} and root_id is null and subject like '%#{search}%'" :
"forum_id = #{forum_id} and root_id is null" "forum_id = #{forum_id} and root_id is null"
elsif search elsif !search.blank?
user_id ? "author_id = #{user_id.to_i} and forum_id in(3, 5) and root_id is null and subject like '%#{search}%'" :
"forum_id in(3, 5) and root_id is null and subject like '%#{search}%'" "forum_id in(3, 5) and root_id is null and subject like '%#{search}%'"
else else
user_id ? "author_id = #{user_id.to_i} and forum_id in(3, 5) and root_id is null" :
"forum_id in(3, 5) and root_id is null" "forum_id in(3, 5) and root_id is null"
end end
@ -41,75 +39,74 @@ class MemosController < ApplicationController
sql += " and all_replies_count != 0" sql += " and all_replies_count != 0"
end end
memos = Memo.field_for_list.includes(:praise_tread, :author).where("#{sql}") memos = Memo.field_for_list.where("#{sql}")
@memos_count = memos.length @memos_count = memos.length
@memos = memos.order("sticky = 1 desc, #{Memo.table_name}.#{s_order} desc").offset(offset).limit(15) @memos = memos.order("sticky = 1 desc, #{Memo.table_name}.#{s_order} desc").page(page).per(limit)
@my_memos_count = Memo.user_posts(current_user.try(:id)).count @memos = @memos.includes(:praise_treads, :tag_repertoires, author: :user_extension)
# @my_memos_count = Memo.user_posts(current_user.try(:id)).count
@tags_info = MemoTagRepertoire.find_by_sql("SELECT tag_repertoire_id, tr.name, count(*) cnt @tags_info = MemoTagRepertoire.find_by_sql("SELECT tag_repertoire_id, tr.name, count(*) cnt
FROM memo_tag_repertoires mtr join tag_repertoires tr on FROM memo_tag_repertoires mtr join tag_repertoires tr on
tr.id = mtr.tag_repertoire_id group by tag_repertoire_id order by cnt desc, tr.id = mtr.tag_repertoire_id group by tag_repertoire_id order by cnt desc,
tag_repertoire_id desc limit 9") tag_repertoire_id desc limit 9")
@hot_memos = Memo.field_for_recommend.posts.hot.limit(4) @hot_memos = Memo.field_for_recommend.posts.hot.includes(:tag_repertoires).limit(4)
@recommend_shixuns = DiscussesService.new.recommends
end end
# GET /memos/1
# GET /memos/1.json # GET /memos/1.json
def show def show
# tidding_count = unviewed_tiddings(current_user) if current_user # tidding_count = unviewed_tiddings(current_user) if current_user
@user = current_user @user = current_user
# TODO 附件最后再做
# attachments_list =
@memo.update_column(:viewed_count, @memo.viewed_count+1) @memo.update_column(:viewed_count, @memo.viewed_count+1)
@memos = @memo.reply_for_memo.includes(:praise_tread, :author).order("created_at desc").limit(10) @memos = @memo.reply_for_memo.includes(:praise_treads, author: :user_extension).order("created_at desc").limit(10)
@attachments = @memo.attachments
@recommend_shixuns = DiscussesService.new.recommends
end end
# GET /memos/new # GET /memos/new
def new def new
@csrf_token = session[:_csrf_toke] ||= SecureRandom.base64(32)
@tag_list = TagRepertoire.field_for_list.order("name asc") @tag_list = TagRepertoire.field_for_list.order("name asc")
end end
# GET /memos/1/edit # GET /memos/1/edit
def edit def edit
@tag_list = TagRepertoire.field_for_list.order("name asc")
@memo_tags = @memo.tag_repertoires.field_for_list
@attachments = @memo.attachments
end end
# POST /memos
# POST /memos.json # POST /memos.json
def create def create
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
begin begin
@memo = Memo.new(memo_params) @memo = Memo.new(memo_params)
@memo.author = current_user @memo.author = current_user
# TODO 保存附件
# @memo.save_attachments(params[:attachments]) if params[:attachments]
@memo.save! @memo.save!
Attachment.associate_container(params[:attachment_ids], @memo.id, @memo.class.name)
params[:tags].each do |tag| params[:tags].each do |tag|
MemoTagRepertoire.create(:memo_id => @memo.id, :tag_repertoire_id => tag) MemoTagRepertoire.create!(memo_id: @memo.id, tag_repertoire_id: tag)
end end
@status = 0 normal_status("帖子创建成功")
@message = "帖子创建成功!"
rescue Exception => e rescue Exception => e
@status = -1 tip_exception("帖子创建失败,原因:#{e}")
@message = "帖子创建失败,原因:#{e}"
raise ActiveRecord::Rollback raise ActiveRecord::Rollback
end end
end end
end end
# PATCH/PUT /memos/1
# PATCH/PUT /memos/1.json # PATCH/PUT /memos/1.json
def update def update
respond_to do |format| ActiveRecord::Base.transaction do
if @memo.update(memo_params) begin
format.html { redirect_to @memo, notice: 'Memo was successfully updated.' } @memo.update_attributes!(memo_params)
format.json { render :show, status: :ok, location: @memo } Attachment.associate_container(params[:attachment_ids], @memo.id, @memo.class.name)
else @memo.memo_tag_repertoires.destroy_all
format.html { render :edit } params[:tags].each do |tag|
format.json { render json: @memo.errors, status: :unprocessable_entity } MemoTagRepertoire.create!(memo_id: @memo.id, tag_repertoire_id: tag)
end
normal_status("帖子更新成功")
rescue Exception => e
tip_exception("帖子更新失败,原因:#{e}")
raise ActiveRecord::Rollback
end end
end end
end end
@ -118,21 +115,88 @@ class MemosController < ApplicationController
# DELETE /memos/1.json # DELETE /memos/1.json
def destroy def destroy
@memo.destroy @memo.destroy
respond_to do |format| normal_status("删除成功")
format.html { redirect_to memos_url, notice: 'Memo was successfully destroyed.' } end
format.json { head :no_content }
def sticky_or_cancel
tip_exception("只能对主贴进行置顶操作") unless @memo.parent_id.nil?
begin
@memo.update_attributes!(sticky: !@memo.sticky)
normal_status("更新成功")
rescue Exception => e
tip_exception("更新失败,原因:#{e}")
raise ActiveRecord::Rollback
end
end
def hidden
tip_exception("不能对主贴进行隐藏操作") if @memo.parent_id.nil?
begin
@memo.update_attributes!(hidden: !@memo.hidden)
normal_status("更新成功")
rescue Exception => e
tip_exception("更新失败,原因:#{e}")
raise ActiveRecord::Rollback
end end
end end
def reply
tip_exception("parent_id不能为空") if params[:parent_id].blank?
tip_exception("content不能为空") if params[:content].blank?
ActiveRecord::Base.transaction do
begin
memo = Memo.find_by!(id: params[:parent_id])
reply = Memo.new
reply.content = params[:content]
reply.author = current_user
reply.forum_id = memo.forum_id
reply.subject = memo.subject
reply.root_id = memo.root_id || memo.id
memo.children << reply
m = Memo.find_by!(id: reply.root_id)
m.increment!(:all_replies_count)
normal_status("回复成功")
rescue Exception => e
tip_exception("回复失败,原因:#{e}")
raise ActiveRecord::Rollback
end
end
end
def more_reply
@user = current_user
page = params[:page] || 2
limit = params[:limit] || 10
offset = (page.to_i - 1) * limit
@memos_count = Memo.where(parent_id: @memo.id).count
@memos = Memo.limit(limit).where(parent_id: @memo.id).includes(:author, :praise_treads).order("created_at desc").offset(offset)
end
private private
# Use callbacks to share common setup or constraints between actions. # Use callbacks to share common setup or constraints between actions.
def set_memo def set_memo
@memo = Memo.find(params[:id]) @memo = Memo.find(params[:id])
end end
def owner_or_admin
tip_exception(403, "无权限操作") unless @memo.author == current_user || current_user.admin? || current_user.business?
end
def is_admin
tip_exception(403, "无权限操作") unless current_user.admin? || current_user.business?
end
# Never trust parameters from the scary internet, only allow the white list through. # Never trust parameters from the scary internet, only allow the white list through.
def memo_params def memo_params
params.fetch(:memo, {}) params.require(:memo).permit(:subject, :content, :forum_id)
end
def validate_memo_params
tip_exception("话题名称不能为空") if params[:subject].blank?
tip_exception("话题内容不能为空") if params[:content].blank?
tip_exception("话题类型不能为空") if params[:forum_id].blank?
tip_exception("技术标签不能为空") if params[:tags].blank?
end end
end end

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

@ -945,12 +945,15 @@ class PollsController < ApplicationController
tip_exception(403,"无权限操作") tip_exception(403,"无权限操作")
elsif (@poll.polls_status == 1) || (@poll_export_questions.size == 0) || (@poll_commit_ids.size == 0) elsif (@poll.polls_status == 1) || (@poll_export_questions.size == 0) || (@poll_commit_ids.size == 0)
normal_status(-1,"暂无用户提交") normal_status(-1,"暂无用户提交")
elsif params[:export].present? && params[:export]
normal_status(0,"正在下载中")
else else
respond_to do |format| respond_to do |format|
format.xlsx{ 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_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) 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
end end

@ -1,10 +1,2 @@
class ProjectsController < ApplicationController class ProjectsController < ApplicationController
def search
query_params = { keyword: params[:keyword], category: 'manage' }
projects = Users::ProjectService.new(current_user, query_params).call
params[:limit] = params[:per_page].to_i.zero? ? 20 : params[:per_page].to_i
@count = projects.count
@projects = paginate projects
end
end end

@ -714,6 +714,7 @@ class ShixunsController < ApplicationController
end end
end end
rescue Exception => e rescue Exception => e
logger.info("shixun_exec error: #{e.message}")
if e.message != "ActiveRecord::RecordInvalid" if e.message != "ActiveRecord::RecordInvalid"
logger.error("##########project_fork error #{e.message}") logger.error("##########project_fork error #{e.message}")
@myshixun.destroy! @myshixun.destroy!

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

@ -26,11 +26,24 @@ class Users::BaseController < ApplicationController
render_forbidden render_forbidden
end 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) def paginate(objs, **opts)
page = params[:page].to_i <= 0 ? 1 : params[:page].to_i page = page_value
per_page = params[:per_page].to_i > 0 ? params[:per_page].to_i : 20 per_page = per_page_value
return Kaminari.paginate_array(objs).page(page).per(per_page) unless observed_logged_user? && opts[:special] return Kaminari.paginate_array(objs).page(page).per(per_page) unless opts[:special] && observed_logged_user?
# note: 为实现第一页少一条记录,让前端放置新建入口 # note: 为实现第一页少一条记录,让前端放置新建入口
if page == 1 if page == 1

@ -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

@ -1,4 +1,6 @@
class Users::ProjectsController < Users::BaseController class Users::ProjectsController < Users::BaseController
skip_before_action :check_observed_user_exists!, only: [:search]
def index def index
projects = Users::ProjectService.new(observed_user, query_params).call projects = Users::ProjectService.new(observed_user, query_params).call
@ -6,6 +8,15 @@ class Users::ProjectsController < Users::BaseController
@projects = paginate(projects.includes(:project_score, owner: { user_extension: :school }), special: true) @projects = paginate(projects.includes(:project_score, owner: { user_extension: :school }), special: true)
end end
def search
query_params = { keyword: params[:keyword], category: 'manage' }
projects = Users::ProjectService.new(current_user, query_params).call
params[:limit] = params[:per_page].to_i.zero? ? 20 : params[:per_page].to_i
@count = projects.count
@projects = paginate projects
end
private private
def query_params def query_params

@ -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

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

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

@ -329,6 +329,23 @@ module ApplicationHelper
content content
end end
def strip_html(text, len=0, endss="...")
ss = ""
if !text.nil? && text.length>0
ss=text.gsub(/<\/?.*?>/, '').strip
ss = ss.gsub(/&nbsp;*/, '')
ss = ss.gsub(/\r\n/,'') #新增
ss = ss.gsub(/\n/,'') #新增
if len > 0 && ss.length > len
ss = ss[0, len] + endss
elsif len > 0 && ss.length <= len
ss = ss
#ss = truncate(ss, :length => len)
end
end
ss
end
def strip_export_title(content) def strip_export_title(content)
con_ = "" con_ = ""
if content.length > 0 if content.length > 0

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

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

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

@ -1,5 +1,6 @@
class Challenge < ApplicationRecord class Challenge < ApplicationRecord
# difficulty: 关卡难度: 1.简单 2.中等 3.困难 # 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") } default_scope { order("challenges.position asc") }
belongs_to :shixun, :touch => true, counter_cache: true belongs_to :shixun, :touch => true, counter_cache: true

@ -8,6 +8,7 @@ class Discuss < ApplicationRecord
has_many :praise_treads, as: :praise_tread_object, dependent: :destroy has_many :praise_treads, as: :praise_tread_object, dependent: :destroy
has_many :tidings, as: :container, dependent: :destroy has_many :tidings, as: :container, dependent: :destroy
has_one :praise_tread_cache, as: :object, dependent: :destroy has_one :praise_tread_cache, as: :object, dependent: :destroy
belongs_to :dis, polymorphic: true
belongs_to :challenge belongs_to :challenge
after_create :send_tiding after_create :send_tiding
@ -44,6 +45,10 @@ class Discuss < ApplicationRecord
Discuss.where(parent_id: self.id).includes(:user).reorder(created_at: :asc) Discuss.where(parent_id: self.id).includes(:user).reorder(created_at: :asc)
end end
def child_discuss_count
Discuss.where(root_id: id).count
end
private private
def send_tiding def send_tiding

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

@ -1,10 +1,12 @@
class Memo < ApplicationRecord class Memo < ApplicationRecord
include Searchable::Memo 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 :tag_repertoires, :through => :memo_tag_repertoires
has_many :praise_tread, as: :praise_tread_object, dependent: :destroy has_many :praise_treads, as: :praise_tread_object, dependent: :destroy
has_one :praise_tread_cache, as: :object, dependent: :destroy has_one :praise_tread_cache, as: :object, dependent: :destroy
belongs_to :author, class_name: 'User', foreign_key: 'author_id' belongs_to :author, class_name: 'User', foreign_key: 'author_id'
@ -12,6 +14,7 @@ class Memo < ApplicationRecord
has_many :descendants, foreign_key: :root_id, class_name: 'Memo' has_many :descendants, foreign_key: :root_id, class_name: 'Memo'
has_many :children, foreign_key: :parent_id, class_name: 'Memo' has_many :children, foreign_key: :parent_id, class_name: 'Memo'
has_many :attachments, as: :container, dependent: :destroy
scope :field_for_list, lambda{ scope :field_for_list, lambda{
select([:id, :subject, :author_id, :sticky, :updated_at, :language, :reward, :all_replies_count, :viewed_count, :forum_id]) select([:id, :subject, :author_id, :sticky, :updated_at, :language, :reward, :all_replies_count, :viewed_count, :forum_id])

@ -1,3 +1,9 @@
class PrivateMessage < ApplicationRecord class PrivateMessage < ApplicationRecord
belongs_to :user 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 end

@ -13,9 +13,14 @@ class Tiding < ApplicationRecord
value = container.try(:identifier) value = container.try(:identifier)
end 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) if value.blank? && belong_container_type && Object.const_defined?(belong_container_type)
value = belong_container.try(:identifier) value = belong_container.try(:identifier)
end end
value value
end end
end end

@ -54,7 +54,8 @@ class User < ApplicationRecord
has_one :onclick_time, :dependent => :destroy 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 :tidings, :dependent => :destroy
has_many :games, :dependent => :destroy has_many :games, :dependent => :destroy
@ -439,7 +440,7 @@ class User < ApplicationRecord
end end
def manager_of_memo?(memo) def manager_of_memo?(memo)
id == memo.author_id || admin? id == memo.author_id || admin? || business?
end end
# 是否是项目管理者 # 是否是项目管理者

@ -14,7 +14,7 @@ class BatchExportShixunReportService
end end
def filename 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 end
def zip def zip

@ -148,18 +148,29 @@ class DiscussesService
@discuss = Discuss.select([:id, :hidden, :reward, :dis_type, :dis_id, :position, :challenge_id, :root_id]).find(id) @discuss = Discuss.select([:id, :hidden, :reward, :dis_type, :dis_id, :position, :challenge_id, :root_id]).find(id)
end end
protected def recommends
hot_shixuns = Shixun.field_for_recommend.published.order("myshixuns_count desc").limit(2)
newest_shixuns = Shixun.field_for_recommend.published.order("created_at desc").limit(2)
recommend_shixuns = []
hot_shixuns.each do |hs|
recommend_shixuns << {:id => hs.id, :name => hs.name, :identifier => hs.identifier, :myshixuns_count => hs.myshixuns_count, :image_url => url_to_avatar(hs)}
end
newest_shixuns.each do |ns|
recommend_shixuns << {:id => ns.id, :name => ns.name, :identifier => ns.identifier, :myshixuns_count => ns.myshixuns_count, :image_url => url_to_avatar(ns)}
end
return recommend_shixuns
end
def memo_list memos def memo_list memos
memos.map do |m| memos.map do |m|
user = User.find(m.user_id) user = m.user
praise_count = m.praise_tread.where(:praise_or_tread => 1).count # praise_count = m.praise_treads.select{|pt| pt.praise_or_tread == 1}.count
replies_count = Discuss.where(:root_id => m.id).count replies_count = m.child_discuss_count
shixun_tag = m.dis.tag_repertoires.map(&:name) shixun_tag = m.dis.tag_repertoires.map(&:name)
m.attributes.dup.except("user_id", "dis_id", "dis_type", "root_id").merge({ m.attributes.dup.except("user_id", "dis_id", "dis_type", "root_id", "praise_count", "content").merge({
subject: (message_content m.content), subject: (message_content m.content),
username: user.show_name, username: user.full_name,
login: user.login, login: user.login,
praise_count: praise_count,
replies_count: replies_count, replies_count: replies_count,
image_url: url_to_avatar(user), image_url: url_to_avatar(user),
shixun_tag: shixun_tag, shixun_tag: shixun_tag,
@ -168,23 +179,12 @@ class DiscussesService
end end
end end
protected
def format_for_current_user current_user def format_for_current_user current_user
{username: current_user.show_name, login: current_user.login, user_id: current_user.id, image_url: url_to_avatar(current_user), admin: current_user.admin?} {username: current_user.show_name, login: current_user.login, user_id: current_user.id, image_url: url_to_avatar(current_user), admin: current_user.admin?}
end end
def recommends
hot_shixuns = Shixun.field_for_recommend.published.order("myshixuns_count desc").limit(2)
newest_shixuns = Shixun.field_for_recommend.published.order("created_at desc").limit(2)
recommend_shixuns = []
hot_shixuns.each do |hs|
recommend_shixuns << {:id => hs.id, :name => hs.name, :identifier => hs.identifier, :myshixuns_count => hs.myshixuns_count, :image_url => url_to_avatar(hs)}
end
newest_shixuns.each do |ns|
recommend_shixuns << {:id => ns.id, :name => ns.name, :identifier => ns.identifier, :myshixuns_count => ns.myshixuns_count, :image_url => url_to_avatar(ns)}
end
return recommend_shixuns
end
# 将数据库对象转换成哈希对象 # 将数据库对象转换成哈希对象
def object_to_hash objects def object_to_hash objects
objects.map{|o| o.attributes.dup} objects.map{|o| o.attributes.dup}

@ -15,8 +15,8 @@ class ExerciseUserPdfService
end end
def filename def filename
user_course = @course.course_members.find_by(user_id:@ex_user_user.id)&.course_group_name # 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')}" 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" "#{exercise_user_name.strip}.pdf"
end end

@ -10,7 +10,7 @@ class ExportExercisesService
end end
def filename 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" "#{exercise_export_name.strip}.zip"
end end

@ -10,7 +10,7 @@ class ExportShixunReportService
end end
def filename 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 end
def prepare_binding 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? is_create = package.new_record?
raise Error, '类型不存在' unless ProjectPackageCategory.where(id: params[:category_id]).exists? 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 raise Error, '竞标截止时间不能小于当前时间' if params[:deadline_at].present? && params[:deadline_at].to_time < Time.now
@ -25,7 +25,9 @@ class ProjectPackages::SaveService < ApplicationService
end end
ActiveRecord::Base.transaction do 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! package.save!
# 处理附件 # 处理附件

@ -22,7 +22,7 @@ class Projects::ApplyJoinService < ApplicationService
apply.forge_activities.find_or_create_by!(user: user, project: project) apply.forge_activities.find_or_create_by!(user: user, project: project)
notify_project_manager! notify_project_manager!(apply)
end end
# notify_project_owner # notify_project_owner
@ -47,10 +47,13 @@ class Projects::ApplyJoinService < ApplicationService
end end
end end
def notify_project_manager! def notify_project_manager!(apply)
columns = %i[user_id applied_id applied_type status viewed applied_user_id role project_id created_at updated_at] 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| AppliedMessage.bulk_insert(*columns) do |worker|
base_attr = { status: false, viewed: false, applied_user_id: user.id, role: role_value, project_id: project.id } base_attr = {
applied_id: apply.id, applied_type: 'AppliedProject', status: false, viewed: false,
applied_user_id: user.id, role: role_value, project_id: project.id
}
project.manager_members.each do |manager| project.manager_members.each do |manager|
worker.add(base_attr.merge(user_id: manager.user_id)) worker.add(base_attr.merge(user_id: manager.user_id))

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

@ -0,0 +1,27 @@
json.memo_list @memos
# do |memo|
# json.(memo, :id, :updated_at, :reward)
# json.subject message_content(memo.content)
# json.praise_count memo.praises_count
# json.replies_count memo.child_discuss_count
# json.shixun_tag memo.dis.tag_repertoires.map(&:name)
# json.username memo.user.full_name
# json.login memo.user.login
# json.image_url url_to_avatar(memo.user)
# json.tpm_url "/shixuns/#{memo.dis.identifier}/shixun_discuss"
# end
json.memo_count @memo_count
json.hot_memos do
json.array! @hot_memos do |hm|
json.(hm, :id, :subject, :language, :forum_id)
json.replies_count hm.all_replies_count
# json.praise_count hm.praise_tread.praise_count
json.tag hm.tag_repertoires.map(&:name)
end
end
json.hot_tags @hot_tags
json.recommend_shixuns @recommend_shixuns

@ -1 +1,2 @@
json.status 0
json.code @code json.code @code

@ -24,5 +24,29 @@ elsif @type == "txt"
json.contents @contents.html_safe json.contents @contents.html_safe
elsif @type =="qrcode" elsif @type =="qrcode"
json.qrcode_str @qrcode_str 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 end

@ -10,7 +10,7 @@ json.hidden message.hidden
if message.m_parent_id if message.m_parent_id
json.can_delete message.can_delete(identity) json.can_delete message.can_delete(identity)
else else
json.praise_count message.praise_treads.liker.count json.praise_count message.praise_treads.select{|pt| pt.praise_or_tread == 1}.count
json.user_praise message.praise_treads.user_liker(current_user).count json.user_praise message.praise_treads.select{|pt| pt.praise_or_tread == 1 && user_id == current_user.id}.count
json.child_message_count message.m_reply_count json.child_message_count message.m_reply_count
end end

@ -1,8 +1,14 @@
json.(memo, :id, :subject, :is_md, :content, :sticky, :reward, :viewed_count) json.memo do
json.id memo.id
json.subject memo.subject
json.is_md memo.is_md
json.content memo.content
json.sticky memo.sticky
json.reward memo.reward
json.viewed_count memo.viewed_count
json.tag memo.tag_repertoires.map(&:name) json.tag memo.tag_repertoires.map(&:name)
json.time memo.created_at json.time memo.created_at
json.replies_count memo.all_replies_count json.replies_count memo.all_replies_count
json.attachments_list [] json.user_praise memo.praise_treads.user_liker(@user.try(:id)) ? true : false
json.user_praise memo.praise_tread.user_liker(@user.try(:id)) ? true : false json.memo_praise_count memo.praise_treads.liker.count
json.memo_praise_count = memo.praise_tread.liker.count end

@ -1,7 +1,7 @@
json.(memo, :id, :subject, :author_id, :sticky, json.(memo, :id, :subject, :author_id, :sticky,
:updated_at, :language, :reward, :all_replies_count, :updated_at, :language, :reward, :all_replies_count,
:viewed_count, :forum_id) :viewed_count, :forum_id)
json.praise_count memo.praise_tread.praise_count json.praise_count memo.praise_treads.liker.count
json.replies_count memo.all_replies_count json.replies_count memo.all_replies_count
json.tag memo.tag_repertoires.map(&:name) json.tag memo.tag_repertoires.map(&:name)
json.user_name memo.author.full_name json.user_name memo.author.full_name

@ -7,10 +7,10 @@ json.username memo.author.full_name
json.reward memo.reward json.reward memo.reward
json.hidden memo.hidden json.hidden memo.hidden
json.permission @user.manager_of_memo?(memo) json.permission @user.manager_of_memo?(memo)
json.praise_count memo.praise_tread.liker.count json.praise_count memo.praise_treads.select{|pt| pt.praise_or_tread == 1}.count
json.user_praise memo.praise_tread.select{|pt| pt.user_id == @user.id}.length > 0 json.user_praise memo.praise_treads.select{|pt| pt.praise_or_tread == 1 && pt.user_id == @user.id}.length > 0
json.user_login memo.author.login json.user_login memo.author.login
json.admin @user.admin json.admin @user.admin? || @user.business?
json.children do json.children do
json.array! memo.children_of_reply do |child| json.array! memo.children_of_reply do |child|

@ -0,0 +1,7 @@
json.(@memo, :subject, :content, :forum_id)
json.memo_tags @memo_tags
json.attachments @attachments do |attachment|
json.partial! "attachments/attachment_simple", locals: {attachment: attachment}
end
json.tag_list @tag_list
json.forums forum_list

@ -8,22 +8,30 @@
# tidding_count: 消息数 # tidding_count: 消息数
# # # #
json.memo_list do json.memo_list @memos do |memo|
json.array! @memos do |memo| json.(memo, :id, :subject, :sticky,
json.partial! "memos/memo_list", locals: {memo: memo} :updated_at, :language, :reward,
end :viewed_count, :forum_id)
json.praise_count memo.praise_treads.select{|pt| pt.praise_or_tread == 1}.count
json.replies_count memo.all_replies_count
json.tag memo.tag_repertoires.map(&:name)
json.user_name memo.author.full_name
json.login memo.author.login
json.image_url url_to_avatar(memo.author)
end end
json.memo_count @memos_count json.memo_count @memos_count
json.hot_memos do json.hot_memos do
json.array! @hot_memos do |hm| json.array! @hot_memos do |hm|
json.(hm, :id, :subject, :language, :forum_id, :all_replies_count) json.(hm, :id, :subject, :language, :forum_id)
json.replies_count hm.all_replies_count json.replies_count hm.all_replies_count
json.praise_count hm.praise_tread.praise_count # json.praise_count hm.praise_tread.praise_count
json.tag hm.tag_repertoires.map(&:name) json.tag hm.tag_repertoires.map(&:name)
end end
end end
json.hot_tags @tags_info.map{|o| o.attributes.dup.except("cnt", "id")} json.hot_tags @tags_info.map{|o| o.attributes.dup.except("cnt", "id")}
json.recommend_shixuns @recommend_shixuns

@ -0,0 +1,7 @@
json.memo_replies do
json.array! @memos do |memo|
json.partial! "memos/replies_list", memo: memo
end
end
json.memos_count @memos_count

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

@ -1,5 +1,9 @@
json.partial! "memos/memo", memo: @memo json.partial! "memos/memo", memo: @memo
json.attachments_list @attachments do |attachment|
json.partial! "attachments/attachment_simple", locals: {attachment: attachment}
end
json.memo_replies do json.memo_replies do
json.array! @memos do |memo| json.array! @memos do |memo|
json.partial! "memos/replies_list", memo: memo json.partial! "memos/replies_list", memo: memo
@ -9,10 +13,11 @@ end
json.author_info do json.author_info do
json.username @memo.author.full_name json.username @memo.author.full_name
# TODO watched_by 插件没法用,等把lib文件载入后在打开代码 # TODO watched_by 插件没法用,等把lib文件载入后在打开代码
#json.watched @memo.author.watched_by?(@user) json.watched @user.watched?(@memo.author)
json.image_url url_to_avatar(@memo.author) json.image_url url_to_avatar(@memo.author)
json.identity @memo.author.identity json.identity @memo.author.identity
json.login @memo.author.login json.login @memo.author.login
json.user_id @memo.author.id json.user_id @memo.author.id
end end
json.recommend_shixuns @recommend_shixuns

@ -1,4 +1,5 @@
json.username @user.full_name json.username @user.full_name
json.real_name @user.real_name
json.login @user.login json.login @user.login
json.user_id @user.id json.user_id @user.id
json.image_url url_to_avatar(@user) json.image_url url_to_avatar(@user)
@ -7,6 +8,7 @@ json.is_teacher @user.user_extension&.teacher?
json.user_identity @user.identity json.user_identity @user.identity
json.tidding_count 0 json.tidding_count 0
json.user_phone_binded @user.phone.present? json.user_phone_binded @user.phone.present?
json.phone @user.phone
json.profile_completed @user.profile_completed? json.profile_completed @user.profile_completed?
if @course if @course
json.course_identity @course_identity 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

@ -20,7 +20,18 @@ Rails.application.routes.draw do
put 'commons/unhidden', to: 'commons#unhidden' put 'commons/unhidden', to: 'commons#unhidden'
delete 'commons/delete', to: 'commons#delete' delete 'commons/delete', to: 'commons#delete'
resources :memos resources :memos do
member do
post :sticky_or_cancel
post :hidden
get :more_reply
end
collection do
post :reply
end
end
resources :tem_tests resources :tem_tests
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
# #
@ -53,6 +64,11 @@ Rails.application.routes.draw do
resource :grade_records, only: [:show] resource :grade_records, only: [:show]
resource :watch, only: [:create, :destroy] resource :watch, only: [:create, :destroy]
resources :project_packages, only: [:index] 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 end
@ -70,7 +86,7 @@ Rails.application.routes.draw do
get :system_update get :system_update
resource :trial_apply, only: [:create] resource :trial_apply, only: [:create]
resources :projects, only: [] do resources :projects, module: :users, only: [] do
get :search, on: :collection get :search, on: :collection
end end
@ -91,6 +107,7 @@ Rails.application.routes.draw do
end end
end end
end end
resources :users_for_private_messages, only: [:index]
resources :myshixuns, param: :identifier, shallow: true do resources :myshixuns, param: :identifier, shallow: true do
member do member do
@ -216,6 +233,7 @@ Rails.application.routes.draw do
resources :discusses do resources :discusses do
collection do collection do
get :new_message get :new_message
get :forum_discusses
end end
member do member do
@ -697,7 +715,7 @@ Rails.application.routes.draw do
resources :libraries, only: [:index, :show, :create, :update, :destroy] resources :libraries, only: [:index, :show, :create, :update, :destroy]
scope module: :projects do scope module: :projects do
resources :applied_projects, only: [:create] resources :project_applies, only: [:create]
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> <title>EduCoder</title>
<meta name="viewport" content="width=device-width,initial-scale=1"> <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/css/edu-common.css?15254409771">
<link rel="stylesheet" type="text/css" href="/stylesheets/educoder/edu-main.css?1525440977"> <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?1525440977"> <link rel="stylesheet" type="text/css" href="/stylesheets/educoder/edu-all.css?15254409771">
</head> </head>
<body style="" data-gr-c-s-loaded="true"> <body style="" data-gr-c-s-loaded="true">

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -332,8 +332,12 @@ label.infolabel{display: block;float: left;width: 56px;text-align: right;margin-
} }
.shixunDes{font-family: 'panmen-webfont';display: block;position: absolute;height: 100%;width: 100%;text-align: center;line-height: 220px;color: #fff;top: 0px;font-size: 24px;background-color: rgba(5,16,26,0.4);border-radius: 6px 6px 0px 0px;} .shixunDes{font-family: 'panmen-webfont';display: block;position: absolute;height: 100%;width: 100%;text-align: center;line-height: 220px;color: #fff;top: 0px;font-size: 24px;background-color: rgba(5,16,26,0.4);border-radius: 6px 6px 0px 0px;}
/*TPM*/ /*TPM*/
.shixunDetail_top{width: 100%;background-image: url("/images/educoder/shixun-detail.jpg");background-size: 100% 100%;height: 240px; .shixunDetail_top{width: 100%;background-image: url("/images/educoder/shixun-detail.jpg"); height: 240px;
justify-content: center;align-items: center;display: -webkit-flex;} justify-content: center;align-items: center;display: -webkit-flex;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
.task-item{margin-top: 30px;padding-bottom: 30px;border-bottom: 1px solid #eee} .task-item{margin-top: 30px;padding-bottom: 30px;border-bottom: 1px solid #eee}
.task-item:last-child{border-bottom: none;} .task-item:last-child{border-bottom: none;}
.challengeNav a.active{color: #4CACFF;} .challengeNav a.active{color: #4CACFF;}
@ -420,14 +424,23 @@ 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: cover;
background-position: center;
background-repeat: no-repeat;
}
.pathNavLine{position: absolute;bottom: -8px;width: 100%;} .pathNavLine{position: absolute;bottom: -8px;width: 100%;}
.path-nav li{float: left;padding: 0px 30px;height: 42px;} .path-nav li{float: left;padding: 0px 30px;height: 42px;}
.path-nav li a{color:#fff;font-size: 16px;display: block; height: 40px;} .path-nav li a{color:#fff;font-size: 16px;display: block; height: 40px;}
.path-nav li.active a{border-bottom: 3px solid #4CACFF;color:#4CACFF;} .path-nav li.active a{border-bottom: 3px solid #4CACFF;color:#4CACFF;}
/*---实训路径详情----*/ /*---实训路径详情----*/
.subhead{width: 100%;margin-bottom:40px;background-size: 100% 100%;background-image: url("/images/educoder/path-detail.jpg");height: 240px; .subhead{width: 100%;margin-bottom:40px;background-size: 100% 100%;background-image: url("/images/educoder/path-detail.jpg");height: 240px;
justify-content: center;align-items: center;display: -webkit-flex;} justify-content: center;align-items: center;display: -webkit-flex;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
.subhead_content{width: 1200px;margin: 0px auto;} .subhead_content{width: 1200px;margin: 0px auto;}
.pathInfo li{text-align: center;float: left;margin-right: 38px;} .pathInfo li{text-align: center;float: left;margin-right: 38px;}
.pathInfo li span{display: block;} .pathInfo li span{display: block;}
@ -467,7 +480,11 @@ li.li-width7{width: 7%;text-align: left}
/*-----------------------------在线课堂动态----------------------------------*/ /*-----------------------------在线课堂动态----------------------------------*/
.courseHead{width: 100%;margin-bottom:40px;background-size: 100% 100%;background-image: url("/images/educoder/course-detail.jpg");height: 240px; .courseHead{width: 100%;margin-bottom:40px;background-size: 100% 100%;background-image: url("/images/educoder/course-detail.jpg");height: 240px;
justify-content: center;align-items: center;display: -webkit-flex;} justify-content: center;align-items: center;display: -webkit-flex;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
.invite-tip{position: absolute;top: -5px;right: 140px;color: #fff; box-sizing: border-box;width: 170px;text-align: center;border-radius: 2px;background-color: rgba(5,16,26,0.6)} .invite-tip{position: absolute;top: -5px;right: 140px;color: #fff; box-sizing: border-box;width: 170px;text-align: center;border-radius: 2px;background-color: rgba(5,16,26,0.6)}
.inviteTipbtn a{font-size:14px;width: 100%;height: 30px;line-height: 30px;display: block;color: #747A7F;background-color: rgba(5,16,26,0.4)} .inviteTipbtn a{font-size:14px;width: 100%;height: 30px;line-height: 30px;display: block;color: #747A7F;background-color: rgba(5,16,26,0.4)}
.inviteTipbtn a:hover{color: #4cacff!important;} .inviteTipbtn a:hover{color: #4cacff!important;}
@ -3048,3 +3065,354 @@ a.singlepublishtwo{
/*课程目标VS毕业要求指标点*/ /*课程目标VS毕业要求指标点*/
.CourseTargetPoint thead th{vertical-align: baseline;} .CourseTargetPoint thead th{vertical-align: baseline;}
.CourseTargetPoint tbody tr td{vertical-align: top;} .CourseTargetPoint tbody tr td{vertical-align: top;}
/*众包*/
.packinput .ant-input{
height: 50px;
width:749px;
border-color: #E1EDF8 !important;
}
.packinput{
width:749px;
}
.packinput .ant-input-group-addon .ant-btn{
width:140px !important;
font-size: 18px;
height: 50px;
background:rgba(76,172,255,1);
}
.setissues{
width:280px;
height:50px;
background:rgba(76,172,255,1);
border-radius:4px;
margin-left: 15px;
}
.pagetype li{
color:#8F8F8F !important;
}
.maxwidth700{
max-width: 700px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.mbf10{
margin-bottom:-10px;
}
.PackageIndexNEIBanner{
width:1200px;
height:110px;
background:rgba(255,255,255,1);
box-shadow:0px 2px 6px 0px rgba(125,125,125,0.26);
border-radius:8px;
}
.padding110{
padding: 39px 110px 0px;
box-sizing: border-box;
}
.borderccc{
border: 1px solid #ccc;
}
.input-100-40s{
width: 100%;
padding: 5px;
box-sizing: border-box;
}
.fafafas{
background-color: #fafafa!important;
height: 40px;
}
.fafafas:focus{
background-color: #fff!important;
}
.fafas .ant-input{
background-color: #fafafa!important;
height: 40px;
}
.fafas .ant-input:focus{
background-color: #fff!important;
}
.fafas .ant-input-group-addon .ant-btn{
width:140px !important;
font-size: 14px;
height: 40px;
background:rgba(76,172,255,1);
}
.newFormbox .upload_filename{
line-height: 32px;
}
.newFormbox .attachment span{
line-height: 23px;
}
.newFormbox .attachment .remove-upload{
line-height: 28px;
}
.pd26a0{
padding: 26px 26px 16px 26px;
}
.newFormbox .attachment .icon-fujian{
font-size: 14px !important;
line-height: 14px;
margin-top: 9px;
}
.newFormbox{
height:20px
}
.ml24{
margin-left:24px;
}
.defalutCancelbtns{
display: block;
border: 1px solid #4CACFF !important;
background-color: #fff;
color: #4CACFF !important;
width:130px;
height:40px;
text-align: center;
line-height: 40px;
border-radius: 4px;
}
.defalutSubmitbtns{
background-color: #4CACFF;
height:40px;
}
.defalutSubmitbtnmodels{
width:127px;
height:30px;
background-color: #4CACFF;
}
.ant-steps-item-process .ant-steps-item-icon{
background-color: #4CACFF !important;
}
.ant-steps-item-process .ant-steps-item-icon{
background-color: #4CACFF !important;
}
.padding200{
padding: 115px 200px 215px 200px;
}
.fontcircle{
font-size: 80px;
display: inherit;
}
.sumbtongs{
font-size: 24px;
display: inherit;
text-align: center;
}
.terraces{
font-size: 16px;
display: inherit;
text-align: center;
color:#999;
}
.padding251{
padding: 0px 245px;
}
.ant-modal-title{
text-align: center;
}
.ml17{
margin-left: 17px;
}
.project-package-items{
display: -webkit-flex;
display: flex;
flex-direction: row;
margin:0px !important;
padding: 20px;
background: white;
margin-bottom:0px !important;
box-shadow: none !important;
}
.mtf7{
margin-top:-7px;
}
.publicpart.orangeGreen {
border-left: 80px solid #29BD8B;
}
.publicwords{
left: 3px;
top: 18px;
}
.project-packages-list .project-package-items .item-image{
width:100px !important;
}
.height185{
height: 185px;
}
.ContacttheTA{
width: 80px;
height: 26px;
font-size: 14px;
line-height: 26px;
display: block;
border: 1px solid #4CACFF !important;
background-color: #fff;
color: #4CACFF !important;
text-align: center;
border-radius: 4px;
}
.ContacttheTAs{
width: 80px;
height: 26px;
font-size: 14px;
line-height: 24px;
/*display: block;*/
border: 1px solid #fff !important;
background-color: #4CACFF;
color: #fff !important;
text-align: center;
border-radius: 4px;
}
.ml28{
margin-left: 28px;
}
.longboxs{
font-size: 16px;
font-family: MicrosoftYaHei-Bold;
font-weight: bold;
color: rgba(5,16,26,1);
border-left: 4px solid rgba(76,172,255,1);
padding-left: 10px;
margin-bottom: 20px;
}
.padding020{
padding: 0px 20px 20px;
}
.mtf3{
margin-top:-3px;
}
.task-btn-nebules{
background: #fff!important;
color: #4CACFF!important;
border: 1px solid #4CACFF!important;
margin-left: 20px;
cursor: pointer;
display: inline-block;
padding: 0 12px;
letter-spacing: 1px;
text-align: center;
font-size: 14px;
height: 30px;
line-height: 30px;
border-radius: 2px;
}
.packageabsolute{
position: absolute;
right: -16px;
top: -7px;
}
.relativef{
position: relative;
}
.homehove:hover .ptext{
color: #4CACFF!important;
}
.homehove:hover .ContacttheTAs{
display: block;
}
.topsj{
position: absolute;
top: -6px;
}
.bottomsj{
position: absolute;
bottom: -6px;
}
.touchSelect .ant-spin-dot-spin{
margin-top: 30% !important;
}
.pagenoedits{
margin-left: 20px;
color: #ccc;
}
.pagemancenter{
text-align: center;
}
.ml0{
margin-left: 0px;
}
.tabelcli{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 850px;
display: table-cell;
}
.mtf10{
margin-top:-10px;
}
.padding26{
padding: 26px;
box-sizing: border-box;
}
.pd26{
padding: 26px;
}
.pd30a0{
padding: 30px 30px 16px 30px;
}
.shaiContent li.shaiItem:hover span{
color: #fff !important;
}
.shaiContent li.shaiItem:hover i.iconfont{
color: #4CACFF!important
}

@ -51,3 +51,7 @@ html, body {
.ant-progress-textno{ .ant-progress-textno{
color: #f5222d; color: #f5222d;
} }
/* md多空格 */
.markdown-body {
white-space: pre;
}

@ -223,6 +223,13 @@ const Interestpage = Loadable({
loader: () => import('./modules/login/EducoderInteresse'), loader: () => import('./modules/login/EducoderInteresse'),
loading: Loading, loading: Loading,
}) })
//众包创新
const ProjectPackages=Loadable({
loader: () => import('./modules/projectPackages/ProjectPackageIndex'),
loading: Loading,
})
const Messagerouting= Loadable({ const Messagerouting= Loadable({
loader: () => import('./modules/message/js/Messagerouting'), loader: () => import('./modules/message/js/Messagerouting'),
loading: Loading, loading: Loading,
@ -283,20 +290,12 @@ class App extends Component {
<Trialapplicationreview {...this.props} {...this.state}></Trialapplicationreview> <Trialapplicationreview {...this.props} {...this.state}></Trialapplicationreview>
<Addcourses {...this.props} {...this.state}/> <Addcourses {...this.props} {...this.state}/>
<AccountProfile {...this.props} {...this.state}/> <AccountProfile {...this.props} {...this.state}/>
{/*{*/}
{/* isRender === true?*/}
{/* <LoginDialog></LoginDialog> : ""*/}
{/*}*/}
{/*{*/}
{/* isRenders === true?*/}
{/*<Trialapplication></Trialapplication>*/}
{/*:""*/}
{/*}*/}
<Router> <Router>
<Switch> <Switch>
{/*<Route path="/login" component={LoginRegisterPage}/>*/}
{/*/!*众包创新*!/*/}
{/*<Route path={"/crowdsourcing"} component={ProjectPackages}/>*/}
{/*认证*/} {/*认证*/}
<Route path="/account" component={AccountPage}/> <Route path="/account" component={AccountPage}/>
@ -319,9 +318,7 @@ class App extends Component {
render={ render={
(props) => (<UsersInfo {...this.props} {...props} {...this.state} />) (props) => (<UsersInfo {...this.props} {...props} {...this.state} />)
}></Route> }></Route>
{/*<Route*/}
{/* path="/trialapplication" component={Trialapplication}*/}
{/*/>*/}
<Route <Route
path="/changepassword" component={EducoderLogin} path="/changepassword" component={EducoderLogin}
/> />
@ -341,14 +338,6 @@ class App extends Component {
{/*列表页*/} {/*列表页*/}
<Route path="/shixuns" component={TPMShixunsIndexComponent}/> <Route path="/shixuns" component={TPMShixunsIndexComponent}/>
{/* <Route path="/shixunchild" component={TPMShixunchildIndexComponent}>
</Route>
<Route path="/fork_list" component={TPMshixunfork_listIndexComponent}>
</Route> */}
{/*<Route path="/forums" component={ForumsIndexComponent}>*/}
{/*</Route>*/}
{/*实训课程(原实训路径)*/} {/*实训课程(原实训路径)*/}
<Route path="/paths" component={ShixunPaths}></Route> <Route path="/paths" component={ShixunPaths}></Route>
@ -362,16 +351,9 @@ class App extends Component {
{/*课堂*/} {/*课堂*/}
<Route path="/courses" component={CoursesIndex} {...this.props}></Route> <Route path="/courses" component={CoursesIndex} {...this.props}></Route>
{/* 课堂讨论 */} {/* <Route path="/forums" component={ForumsIndexComponent}>
{/* <Route path="/board" component = {BoardIndex} {...this.props}></Route> */}
{/* <Route path="/tpforums" component={TPForumsIndexComponent}>
</Route> */} </Route> */}
{/* <Route path="/myshixuns/:shixunId/stages/:stageId" component={Index}/> */}
{/* 兴趣页面*/}
{/*<Route path="/interest" component={Interestpage}/>*/}
<Route path="/comment" component={CommentComponent}/> <Route path="/comment" component={CommentComponent}/>
<Route path="/testMaterial" component={TestMaterialDesignComponent}/> <Route path="/testMaterial" component={TestMaterialDesignComponent}/>
<Route path="/test" component={TestIndex}/> <Route path="/test" component={TestIndex}/>
@ -379,14 +361,9 @@ class App extends Component {
<Route path="/testRCComponent" component={TestComponent}/> <Route path="/testRCComponent" component={TestComponent}/>
<Route path="/testUrlQuery" component={TestUrlQueryComponent}/> <Route path="/testUrlQuery" component={TestUrlQueryComponent}/>
<Route path="/message" component={Messagerouting}></Route> <Route path="/message" component={Messagerouting}></Route>
{/* <Route component={NotFoundPage}/> */}
{/*列表页*/}
{/*<Route component={TPMShixunsIndexComponent}/>*/}
{/*首页*/}
<Route exact path="/" component={ShixunsHome}/> <Route exact path="/" component={ShixunsHome}/>
<Route component={Shixunnopage}/> <Route component={Shixunnopage}/>
{/*<Route component={ShixunsHome}/>*/}
</Switch> </Switch>
</Router> </Router>

@ -41,7 +41,7 @@ export function initAxiosInterceptors(props) {
// proxy = "http://testbdweb.trustie.net" // proxy = "http://testbdweb.trustie.net"
// proxy = "http://testbdweb.educoder.net" // proxy = "http://testbdweb.educoder.net"
// proxy = "https://testeduplus2.educoder.net" // proxy = "https://testeduplus2.educoder.net"
proxy="http://47.96.87.25:48080/" proxy="http://47.96.87.25:48080"
// 在这里使用requestMap控制避免用户通过双击等操作发出重复的请求 // 在这里使用requestMap控制避免用户通过双击等操作发出重复的请求
// 如果需要支持重复的请求考虑config里面自定义一个allowRepeat参考来控制 // 如果需要支持重复的请求考虑config里面自定义一个allowRepeat参考来控制

@ -1,4 +1,6 @@
import { bytesToSize } from 'educoder'; import { bytesToSize, getUrl2 } from 'educoder';
const $ = window.$
export function isImageExtension(fileName) { export function isImageExtension(fileName) {
return fileName ? !!(fileName.match(/.(jpg|jpeg|png|gif)$/i)) : false return fileName ? !!(fileName.match(/.(jpg|jpeg|png|gif)$/i)) : false
} }
@ -36,6 +38,25 @@ export function markdownToHTML(oldContent, selector) {
return content 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) { export function appendFileSizeToUploadFile(item) {
return `${item.title}${uploadNameSizeSeperator}${item.filesize}` return `${item.title}${uploadNameSizeSeperator}${item.filesize}`

@ -0,0 +1,79 @@
import React,{ Component } from "react";
import { getUrl2 } from "educoder";
const $ = window.$
let _url_origin = getUrl2()
class Clappr extends Component{
constructor(props){
super(props);
this.state={
}
}
componentDidMount() {
const source = this.props.source || "http://your.video/here.mp4"
const { id } = this.props
const _id = `#_player${id}`
if (window['Clappr']) {
const player = new window.Clappr.Player({
source: source, parentId: _id,
plugins: {
'core': [window.Clappr.MediaControl, window.Clappr.Playback]
}
});
} else {
$.getScript(
`${_url_origin}/javascripts/media/clappr.min.js`,
(data, textStatus, jqxhr) => {
window.clappr = window.Clappr
$.getScript(
`${_url_origin}/javascripts/media/clappr-playback-rate-plugin.min.js`,
(data, textStatus, jqxhr) => {
const player = new window.Clappr.Player({
source: source, parentId: _id,
plugins: {
'core': [window.Clappr.MediaControl, window['clappr-playback-rate-plugin'].default]
}
});
})
});
//
// $.when(
// $.getScript( `${_url_origin}/javascripts/media/clappr.min.js` ),
// // $.getScript( `${_url_origin}/javascripts/media/clappr-thumbnails-plugin.js` ),
// $.getScript( `${_url_origin}/javascripts/media/clappr-playback-rate-plugin.min.js` ),
// $.Deferred(function( deferred ){
// $( deferred.resolve );
// })
// ).done(function(){
// //place your code here, the scripts are all loaded
// const player = new window.Clappr.Player({
// source: source, parentId: _id,
// plugins: {
// 'core': [window.Clappr.MediaControl, window.Clappr.Playback]
// }
// });
// });
}
}
render(){
let { source, id, className } = this.props;
const _id = `_player${id}`
return(
<React.Fragment>
<style>{`
.playback_rate {
margin-right: 16px;
}
`}</style>
<div id={_id} className={className}></div>
</React.Fragment>
)
}
}
export default Clappr;

@ -15,7 +15,8 @@ export { updatePageParams as updatePageParams } from './RouterUti
export { bytesToSize as bytesToSize } from './UnitUtil'; 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' export { handleDateString, getNextHalfHourOfMoment,formatDuring } from './DateUtil'
@ -52,6 +53,8 @@ export { default as MarkdownToHtml } from './components/markdown/MarkdownToHtml'
export { default as DMDEditor } from './components/markdown/DMDEditor' export { default as DMDEditor } from './components/markdown/DMDEditor'
export { default as Clappr } from './components/media/Clappr'
export { default as ImageLayerHook } from './hooks/ImageLayerHook' export { default as ImageLayerHook } from './hooks/ImageLayerHook'

@ -0,0 +1,50 @@
import React, { Component } from 'react';
class EffectDisplayContent extends Component {
constructor(props) {
super(props)
this.state = {
}
}
render() {
const { typeName, content1, content2, content3 } = this.props;
return (
<div className="task-popup-content effectDisplay">
<style>{`
.effectDisplay .content_title {
flex: 1 1 0
}
.effectDisplay .content>div {
flex: 1
}
.effectDisplay .clappr {
display: flex;
justify-content: center;
}
.effectDisplay .clappr>div {
width: 400px !important;
}
`}</style>
<div className="clearfix df">
{content1 && <p className="content_title edu-txt-center fl mr03precent font-18">原始{typeName}</p>}
{content2 && <p className="content_title edu-txt-center fl font-18 mr03precent">实际输出{typeName}</p>}
{content3 && <p className="content_title edu-txt-center fl font-18 mr03precent">预期输出{typeName}</p>}
</div>
<div className="clearfix df content" >
{content1 && <div className="fl mr03precent pt10 mb50">
{content1}
</div>}
{content2 && <div className="fl mr03precent pt10 mb50">
{content2}
</div>}
{content3 && <div className="fl mr03precent pt10 mb50">
{content3}
</div>}
</div>
</div>
);
}
}
export default EffectDisplayContent;

@ -2,9 +2,9 @@ import React, { Component } from 'react';
import { Redirect } from 'react-router'; import { Redirect } from 'react-router';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Clappr } from 'educoder'
import axios from 'axios'; import axios from 'axios';
import EffectDisplayContent from './EffectDisplayContent'
class EvaluateSuccessEffectDisplay extends Component { class EvaluateSuccessEffectDisplay extends Component {
constructor(props) { constructor(props) {
super(props) super(props)
@ -35,7 +35,8 @@ class EvaluateSuccessEffectDisplay extends Component {
// qrcode // qrcode
// const type = 'image' // 'qrcode' // const type = 'image' // 'qrcode'
const { type, qrcode_str, const { type, qrcode_str,
answer_picture, orignal_picture, user_picture, contents } = this.props; answer_picture, orignal_picture, user_picture, contents,
user_file, answer_file, orignal_file } = this.props;
if (type == 'qrcode') { if (type == 'qrcode') {
// 单张图片比如安卓评测完显示qrcode // 单张图片比如安卓评测完显示qrcode
return ( return (
@ -54,22 +55,35 @@ class EvaluateSuccessEffectDisplay extends Component {
return ( return (
<div className="task-popup-content"> <div className="task-popup-content">
<div className="clearfix"> <div className="clearfix">
<p className="edu-txt-center fl with33 mr03precent font-18">原始图片</p> {orignal_picture[0] && <p className="edu-txt-center fl with33 mr03precent font-18">原始图片</p>}
<p className="edu-txt-center fl font-18 with33 mr03precent">实际输出图片</p> <p className="edu-txt-center fl font-18 with33 mr03precent">实际输出图片</p>
<p className="edu-txt-center fl font-18 with33 mr03precent">预期输出图片</p> <p className="edu-txt-center fl font-18 with33 mr03precent">预期输出图片</p>
</div> </div>
<div className="clearfix" id="picture-content"> <div className="clearfix" id="picture-content">
{orignal_picture[0] && <div className="fl with33 mr03precent pt10 mb50">
{orignal_picture.map(item => {
return (
<img alt="Icon"
src={ item.pic_url}/> )
})}
{/* {orignal_picture[0] && <img alt="Icon"
src={ orignal_picture[0].pic_url}/>} */}
</div>}
<div className="fl with33 mr03precent pt10 mb50"> <div className="fl with33 mr03precent pt10 mb50">
{orignal_picture[0] && <img alt="Icon" {user_picture.map(item => {
src={ orignal_picture[0].pic_url}/>} return (
</div> <img alt="Icon"
<div className="fl with33 mr03precent pt10 mb50"> src={ item.pic_url}/> )
{user_picture[0] && <img alt="Icon" })}
src={ user_picture[0].pic_url }/>}
</div> </div>
<div className="fl with33 mr03precent pt10 mb50"> <div className="fl with33 mr03precent pt10 mb50">
{ answer_picture[0] && <img alt="Icon" {answer_picture.map(item => {
src={ answer_picture[0].pic_url}/> } return (
<img alt="Icon"
src={ item.pic_url}/> )
})}
{/* { answer_picture[0] && <img alt="Icon"
src={ answer_picture[0].pic_url}/> } */}
</div> </div>
</div> </div>
</div> </div>
@ -88,6 +102,30 @@ class EvaluateSuccessEffectDisplay extends Component {
return ( return (
<iframe id="_displayIframe"></iframe> <iframe id="_displayIframe"></iframe>
) )
} else if (type == 'mp3') {
return (
<EffectDisplayContent
typeName="音频"
content1={ orignal_file[0] && orignal_file[0].file_url
? <Clappr source={orignal_file[0].file_url} id="1" className="clappr"></Clappr> : null }
content2={ user_file[0] && user_file[0].file_url
? <Clappr source={user_file[0].file_url} id="2" className="clappr"></Clappr> : null }
content3={ answer_file[0] && answer_file[0].file_url
? <Clappr source={answer_file[0].file_url} id="3" className="clappr"></Clappr> : null }
></EffectDisplayContent>
)
} else if (type == 'mp4') {
return (
<EffectDisplayContent
typeName="视频"
content1={ orignal_file[0] && orignal_file[0].file_url
? <Clappr source={orignal_file[0].file_url} id="1" className="clappr"></Clappr> : null }
content2={ user_file[0] && user_file[0].file_url
? <Clappr source={user_file[0].file_url} id="2" className="clappr"></Clappr> : null }
content3={ answer_file[0] && answer_file[0].file_url
? <Clappr source={answer_file[0].file_url} id="3" className="clappr"></Clappr> : null }
></EffectDisplayContent>
)
} }
/* <div className="with49 fr"> /* <div className="with49 fr">

@ -8,7 +8,7 @@ import { CNotificationHOC } from '../common/CNotificationHOC'
import { RouteHOC } from './common' import { RouteHOC } from './common'
import locale from 'antd/lib/date-picker/locale/zh_CN'; 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 axios from 'axios';
import Modals from '../../modals/Modals'; import Modals from '../../modals/Modals';
import CoursesListType from '../coursesPublic/CoursesListType'; import CoursesListType from '../coursesPublic/CoursesListType';
@ -18,6 +18,8 @@ import '../css/Courses.css'
import CBreadcrumb from '../common/CBreadcrumb' import CBreadcrumb from '../common/CBreadcrumb'
import DownloadMessageysl from "../../modals/DownloadMessageysl"; import DownloadMessageysl from "../../modals/DownloadMessageysl";
import { Spin } from 'antd'
//引入对应跳转的组件 //引入对应跳转的组件
//新建分组/普通作业 //新建分组/普通作业
@ -64,6 +66,7 @@ class CommonWorkDetailIndex extends Component{
this.state = { this.state = {
DownloadType:false, DownloadType:false,
DownloadMessageval:undefined, DownloadMessageval:undefined,
donwloading:false,
} }
} }
initWorkDetailCommonState = (data) => { initWorkDetailCommonState = (data) => {
@ -103,7 +106,10 @@ class CommonWorkDetailIndex extends Component{
/// 确认是否下载 /// 确认是否下载
confirmysl(url){ 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){ if(response.data.status&&response.data.status===-1){
}else if(response.data.status&&response.data.status===-2){ }else if(response.data.status&&response.data.status===-2){
@ -123,7 +129,20 @@ class CommonWorkDetailIndex extends Component{
} }
}else { }else {
this.props.showNotification(`正在下载中`); 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) => { }).catch((error) => {
console.log(error) console.log(error)
@ -171,8 +190,10 @@ class CommonWorkDetailIndex extends Component{
let params = {} let params = {}
if (isListModule) { if (isListModule) {
// TODO // TODO
if(this.child!=undefined) {
params = this.child._getRequestParams() !== undefined ? this.child._getRequestParams() : {}; params = this.child._getRequestParams() !== undefined ? this.child._getRequestParams() : {};
} }
}
// console.log("普通作业176176176"); // console.log("普通作业176176176");
// console.log(params); // console.log(params);
let exportUrl = `/homework_commons/${workId}/works_list.zip?${queryString.stringify(params)}` let exportUrl = `/homework_commons/${workId}/works_list.zip?${queryString.stringify(params)}`
@ -246,7 +267,7 @@ class CommonWorkDetailIndex extends Component{
} }
</div> </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"> <div className="mt10 clearfix edu-back-white poll_list pl5">
<Link <Link
onClick={() => this.setState({moduleName: '作品列表'})} onClick={() => this.setState({moduleName: '作品列表'})}
@ -285,18 +306,30 @@ class CommonWorkDetailIndex extends Component{
padding-top: 10px; padding-top: 10px;
padding-bottom: 8px; padding-bottom: 8px;
} }
.floatSpinParent .ant-spin-nested-loading {
float: right;
}
`}</style> `}</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> 导出<i className="iconfont icon-xiajiantou font-12 ml2"></i>
<ul className="drop_down_menu" style={{"right":"-34px","left":"unset","height":"auto"}}> <ul className="drop_down_menu" style={{"right":"-34px","left":"unset","height":"auto"}}>
<li><a href={"javascript:void(0)"} className="color-dark" <li>
onClick={() => this.confirmysl(exportResultUrl, exportParams)}
>导出成绩</a></li> <a href={"javascript:void(0)"} className="color-dark"
<li><a href={"javascript:void(0)"} className="color-dark" onClick={() => this.confirmysl(exportResultUrl)}
onClick={() => this.confirmysl(exportUrl, exportParams)} >导出成绩</a>
>导出作品附件</a></li>
</li>
<li>
<a href={"javascript:void(0)"} className="color-dark"
onClick={() => this.confirmysl(exportUrl)}
>导出作品附件</a>
</li>
</ul> </ul>
</li>:""} </li>
</Spin>:""}
{/* {isAdmin && <a className={"fr color-blue font-16"} href={exportUrl}></a>} {/* {isAdmin && <a className={"fr color-blue font-16"} href={exportUrl}></a>}
{isAdmin && <a className={"fr color-blue font-16"} href={exportResultUrl}>导出成绩</a>} */} {isAdmin && <a className={"fr color-blue font-16"} href={exportResultUrl}>导出成绩</a>} */}

@ -425,7 +425,8 @@ class CommonWorkList extends Component{
teacher_comment: arg_teacher_comment.length == 0 ? '' : arg_teacher_comment[0], teacher_comment: arg_teacher_comment.length == 0 ? '' : arg_teacher_comment[0],
order, order,
limit: PAGE_SIZE, limit: PAGE_SIZE,
b_order: orderMap[order] b_order: orderMap[order],
group_id:arg_course_group,
} }
} }
fetchList = () => { fetchList = () => {

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

@ -3,7 +3,9 @@
height: 300px; height: 300px;
background-image: url(./courses.jpg); background-image: url(./courses.jpg);
background-color: #081C4B; background-color: #081C4B;
background-size: 100% 100%; background-size: cover;
background-position: center;
background-repeat: no-repeat;
} }
a{ a{
text-decoration: none; text-decoration: none;

@ -20,6 +20,14 @@ function disabledDateTime() {
// disabledSeconds: () => [55, 56], // disabledSeconds: () => [55, 56],
}; };
} }
function disabledDate(current) {
return current && current < moment().endOf('day').subtract(1, 'days');
}
class HomeworkModal extends Component{ class HomeworkModal extends Component{
constructor(props){ constructor(props){
super(props); super(props);
@ -81,7 +89,7 @@ class HomeworkModal extends Component{
// console.log('startValue',dateString); // console.log('startValue',dateString);
this.setState({ this.setState({
endtime: handleDateString(dateString), endtime: date===null?"":handleDateString(dateString),
}) })
} }
@ -188,6 +196,7 @@ class HomeworkModal extends Component{
dropdownClassName="hideDisable" dropdownClassName="hideDisable"
showTime={{ format: 'HH:mm' }} showTime={{ format: 'HH:mm' }}
disabledTime={disabledDateTime} disabledTime={disabledDateTime}
disabledDate={disabledDate}
showToday={false} showToday={false}
locale={locale} locale={locale}
format={dateFormat} format={dateFormat}

@ -16,12 +16,17 @@ function range(start, end) {
} }
return result; return result;
} }
function disabledDateTime() { function disabledDateTime() {
return { return {
disabledMinutes: () => range(1, 30).concat(range(31, 60)), disabledMinutes: () => range(1, 30).concat(range(31, 60)),
// disabledSeconds: () => range(1,60) // disabledSeconds: () => range(1,60)
} }
} }
function disabledDate(current) {
return current && current < moment().endOf('day').subtract(1, 'days');
}
class Selectsetting extends Component{ class Selectsetting extends Component{
constructor(props){ constructor(props){
super(props); super(props);
@ -698,6 +703,7 @@ class Selectsetting extends Component{
onChange={(e,index)=>this.onChangeTimepublishs(e,index,key)} onChange={(e,index)=>this.onChangeTimepublishs(e,index,key)}
// onChange={ this.onChangeTimepublish } // onChange={ this.onChangeTimepublish }
disabledTime={disabledDateTime} disabledTime={disabledDateTime}
disabledDate={disabledDate}
/> />
{key!=0?<i className="iconfont icon-shanchu color-grey-c font-14 font-n ml20" onClick={()=>this.deletegrouppublish(key)}></i>:""} {key!=0?<i className="iconfont icon-shanchu color-grey-c font-14 font-n ml20" onClick={()=>this.deletegrouppublish(key)}></i>:""}
@ -730,7 +736,7 @@ class Selectsetting extends Component{
value={datatime===undefined||datatime===""?"":moment(datatime, dateFormat)} value={datatime===undefined||datatime===""?"":moment(datatime, dateFormat)}
onChange={this.onChangeTimepublish} onChange={this.onChangeTimepublish}
disabledTime={disabledDateTime} disabledTime={disabledDateTime}
disabledDate={disabledDate}
/> />
</span> </span>
</p>:""} </p>:""}

@ -22,6 +22,11 @@ function disabledDateTime() {
}; };
} }
function disabledDate(current) {
return current && current < moment().endOf('day').subtract(1, 'days');
}
const dateFormat="YYYY-MM-DD HH:mm"; const dateFormat="YYYY-MM-DD HH:mm";
class Sendresource extends Component{ class Sendresource extends Component{
constructor(props){ constructor(props){
@ -525,6 +530,7 @@ class Sendresource extends Component{
onChange={(e,index)=>this.onChangeTimepublish(e,index,key,2)} onChange={(e,index)=>this.onChangeTimepublish(e,index,key,2)}
// onChange={ this.onChangeTimepublish } // onChange={ this.onChangeTimepublish }
disabledTime={disabledDateTime} disabledTime={disabledDateTime}
disabledDate={disabledDate}
/> />
{key!=0?<i className="iconfont icon-shanchu color-grey-c font-14 font-n ml20" onClick={()=>this.deletegrouppublish(key)}></i>:""} {key!=0?<i className="iconfont icon-shanchu color-grey-c font-14 font-n ml20" onClick={()=>this.deletegrouppublish(key)}></i>:""}
{key===course_group_publish_times.length-1&&key<this.state.course_groups_count-1?<i className="iconfont icon-tianjiafangda color-green ml15" onClick={this.addgrouppublish}></i>:""} {key===course_group_publish_times.length-1&&key<this.state.course_groups_count-1?<i className="iconfont icon-tianjiafangda color-green ml15" onClick={this.addgrouppublish}></i>:""}
@ -551,7 +557,7 @@ class Sendresource extends Component{
value={datatime===undefined||datatime===""?undefined:moment(datatime, dateFormat)} value={datatime===undefined||datatime===""?undefined:moment(datatime, dateFormat)}
onChange={(e,index)=>this.onChangeTimepublish(e,index,undefined,1)} onChange={(e,index)=>this.onChangeTimepublish(e,index,undefined,1)}
disabledTime={disabledDateTime} disabledTime={disabledDateTime}
disabledDate={disabledDate}
/> />
</span> </span>
</p>:""} </p>:""}

@ -33,6 +33,21 @@ class ExerciseDisplay extends Component{
this.state = { this.state = {
exercise_questions: [], 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 = () => { componentDidMount = () => {
@ -49,6 +64,21 @@ class ExerciseDisplay extends Component{
console.log(error); 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() { render() {
// let { question_title, question_score, question_type, question_choices, standard_answer, // let { question_title, question_score, question_type, question_choices, standard_answer,

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

Loading…
Cancel
Save