Merge branch 'dev_aliyun' into dev_Cs

# Conflicts:
#	public/react/src/App.js
dev_hjm
caishi 6 years ago
commit 1acd1d2d27

@ -9,8 +9,7 @@ class BiddingUsersController < ApplicationController
end end
def win def win
package = current_user.project_packages.find(params[:project_package_id]) ProjectPackages::WinBiddingService.call(current_package, current_user, params)
ProjectPackages::WinBiddingService.call(package, params)
render_ok render_ok
rescue ProjectPackages::WinBiddingService::Error => ex rescue ProjectPackages::WinBiddingService::Error => ex
render_error(ex.message) render_error(ex.message)

@ -234,6 +234,8 @@ class CoursesController < ApplicationController
def destroy def destroy
if @course.is_delete == 0 if @course.is_delete == 0
@course.delete! @course.delete!
Tiding.create!(user_id: @course.tea_id, trigger_user_id: 1, container_id: @course.id,
container_type: 'Course', tiding_type: 'Delete', extra: @course.name)
normal_status(0, "成功") normal_status(0, "成功")
else else
normal_status(-1, "课堂已删除,无需重复操作") normal_status(-1, "课堂已删除,无需重复操作")
@ -296,7 +298,13 @@ class CoursesController < ApplicationController
@applications = CourseMessage.unhandled_join_course_requests_by_course(@course). @applications = CourseMessage.unhandled_join_course_requests_by_course(@course).
joins("join users on course_messages.course_message_id=users.id"). joins("join users on course_messages.course_message_id=users.id").
where("LOWER(concat(users.lastname, users.firstname)) LIKE ?", "%#{search_str}%") where("LOWER(concat(users.lastname, users.firstname)) LIKE ?", "%#{search_str}%")
@teacher_list_size = @course.teachers.size if @course.try(:id) != 1309 || current_user.admin? || current_user.try(:id) == 15582
teacher_list = @course.course_members.where("course_members.role in (1, 2, 3)")
else
teacher_list = @course.course_members.where("(course_members.role in (1, 3) or (course_members.user_id = #{current_user.id}
and course_members.role = 2))")
end
@teacher_list_size = teacher_list.size
@applications_size = CourseMessage.unhandled_join_course_requests_by_course(@course).size @applications_size = CourseMessage.unhandled_join_course_requests_by_course(@course).size
@is_admin = @user_course_identity < Course::PROFESSOR @is_admin = @user_course_identity < Course::PROFESSOR
end end
@ -909,6 +917,7 @@ class CoursesController < ApplicationController
CourseAddStudentCreateWorksJob.perform_later(course.id, [current_user.id]) CourseAddStudentCreateWorksJob.perform_later(course.id, [current_user.id])
StudentJoinCourseNotifyJob.perform_later(current_user.id, course.id) StudentJoinCourseNotifyJob.perform_later(current_user.id, course.id)
end end
student_role = 1
end end
# 创建教师身份 # 创建教师身份
@ -921,24 +930,33 @@ class CoursesController < ApplicationController
course_message = CourseMessage.new(course_id: course.id, user_id: course.tea_id, status: 0, course_message = CourseMessage.new(course_id: course.id, user_id: course.tea_id, status: 0,
course_message_id: current_user.id, course_message_type: "JoinCourseRequest", course_message_id: current_user.id, course_message_type: "JoinCourseRequest",
viewed: false) viewed: false)
course_message.content = 2 if params[:professor].present? && params[:professor].to_i == 1 if params[:professor].present? && params[:professor].to_i == 1
course_message.content = 3 if params[:assistant_professor].present? && params[:assistant_professor].to_i == 1 course_message.content = 2
role = 9
message = "教师申请已提交,请等待审核"
else
course_message.content = 3
role = 7
message = "助教申请已提交,请等待审核"
end
course_message.save! course_message.save!
role = course_message.content == 2 ? '7' : '9' # 7:助教 9:教师 # role = course_message.content == 2 ? '9' : '7' # 7:助教 9:教师
ApplyTeacherRoleJoinCourseNotifyJob.perform_later(current_user.id, course.id, role) ApplyTeacherRoleJoinCourseNotifyJob.perform_later(current_user.id, course.id, role)
message = "#{course_message.content == 2 ? '助教' : '教师'}申请已提交,请等待审核" # message = "#{course_message.content == 2 ? '教师' : '助教'}申请已提交,请等待审核"
else else
message = "#{existing_course_message.content == 2 ? '助教' : '教师'}申请已提交,请等待审核" message = "#{existing_course_message.content == '2' ? '教师' : '助教'}申请已提交,请等待审核"
end end
else
message = "您已是课堂成员"
end end
teacher_role = 1 teacher_role = 1
end end
if teacher_role && current_user.student_of_course?(course) if teacher_role && student_role
render json: { status: 0, message: message, course_id: course.id} render json: { status: 0, message: message, course_id: course.id}
elsif current_user.student_of_course?(course) elsif student_role
render json: { status: 0, message: "加入成功", course_id: course.id} render json: { status: 0, message: "加入成功", course_id: course.id}
else else
normal_status(message) normal_status(message)

@ -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, :forum_discusses] before_action :find_discuss, except: [:create, :index, :new_message, :reward_code, :forum_discusses, :plus]
def index def index
page = params[:page].to_i page = params[:page].to_i

@ -703,12 +703,14 @@ class ExercisesController < ApplicationController
end end
if ex_status == 1 #如果试卷存在已发布的,或者是已截止的,那么则直接跳过 if ex_status == 1 #如果试卷存在已发布的,或者是已截止的,那么则直接跳过
g_course = params[:group_ids] #表示是否传入分班参数,如果传入分班的参数,那么试卷的统一设置需修改 g_course = params[:group_ids] #表示是否传入分班参数,如果传入分班的参数,那么试卷的统一设置需修改
tiding_group_ids = g_course
if g_course if g_course
user_course_groups = @course.charge_group_ids(current_user) user_course_groups = @course.charge_group_ids(current_user)
if g_course.map(&:to_i).sort == user_course_groups.sort # 如果是设置为全部班级,则试卷不用分组,且试卷设定为统一设置,否则则分组设置 if g_course.map(&:to_i).sort == user_course_groups.sort # 如果是设置为全部班级,则试卷不用分组,且试卷设定为统一设置,否则则分组设置
exercise.exercise_group_settings.destroy_all exercise.exercise_group_settings.destroy_all
ex_unified = true ex_unified = true
e_time = ex_end_time e_time = ex_end_time
tiding_group_ids = []
else else
ex_unified = false ex_unified = false
g_course.each do |i| g_course.each do |i|
@ -748,7 +750,7 @@ class ExercisesController < ApplicationController
if exercise.course_acts.size == 0 if exercise.course_acts.size == 0
exercise.course_acts << CourseActivity.new(:user_id => exercise.user_id,:course_id => exercise.course_id) exercise.course_acts << CourseActivity.new(:user_id => exercise.user_id,:course_id => exercise.course_id)
end end
ExercisePublishNotifyJob.perform_later(exercise.id, g_course) ExercisePublishNotifyJob.perform_later(exercise.id, tiding_group_ids)
end end
end end
end end
@ -1084,6 +1086,7 @@ class ExercisesController < ApplicationController
:subjective_score => subjective_score :subjective_score => subjective_score
} }
@answer_committed_user.update_attributes(commit_option) @answer_committed_user.update_attributes(commit_option)
CommitExercsieNotifyJobJob.perform_later(@exercise.id, current_user.id)
normal_status(0,"试卷提交成功!") normal_status(0,"试卷提交成功!")
end end
rescue Exception => e rescue Exception => e

@ -17,8 +17,8 @@ class FilesController < ApplicationController
sort_type = params[:sort_type] || 'created_on' # created_on时间排序 downloads下载次数排序; quotes: 引用次数排序 sort_type = params[:sort_type] || 'created_on' # created_on时间排序 downloads下载次数排序; quotes: 引用次数排序
course_second_category_id = params[:course_second_category_id] || 0 # 0: 为主目录, 其他为次目录id course_second_category_id = params[:course_second_category_id] || 0 # 0: 为主目录, 其他为次目录id
@user = current_user @user = current_user
@attachments = @course.attachments.by_course_second_category_id(course_second_category_id) @attachments = course_second_category_id.to_i == 0 ? @course.attachments : @course.attachments.by_course_second_category_id(course_second_category_id)
.includes(attachment_group_settings: :course_group, author: [:user_extension, :course_members]) @attachments = @attachments.includes(attachment_group_settings: :course_group, author: [:user_extension, :course_members])
.ordered(sort: sort.to_i, sort_type: sort_type.strip) .ordered(sort: sort.to_i, sort_type: sort_type.strip)
get_category(@course, course_second_category_id) get_category(@course, course_second_category_id)

@ -22,7 +22,7 @@ class LibrariesController < ApplicationController
end end
@count = libraries.count @count = libraries.count
@libraries = paginate libraries.includes(:library_tags, :praise_tread_cache, user: :user_extension) @libraries = paginate libraries.includes(:library_tags, user: :user_extension)
ids = @libraries.map(&:id) ids = @libraries.map(&:id)
@download_count_map = Attachment.where(container_type: 'Library', container_id: ids) @download_count_map = Attachment.where(container_type: 'Library', container_id: ids)

@ -3,7 +3,7 @@ class MemosController < ApplicationController
before_action :set_memo, only: [:show, :edit, :update, :destroy, :sticky_or_cancel, :hidden, :more_reply] 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 :validate_memo_params, only: [:create, :update]
before_action :owner_or_admin, only: [:edit, :update, :destroy] before_action :owner_or_admin, only: [:edit, :update, :destroy]
before_action :is_admin, only: [:sticky_or_cancel, :hidden] before_action :require_business, only: [:sticky_or_cancel, :hidden]
include ApplicationHelper include ApplicationHelper
# GET /memos # GET /memos
@ -85,7 +85,7 @@ class MemosController < ApplicationController
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
normal_status("帖子创建成功") render :json => {memo_id: @memo.id, status: 0, message: "帖子创建成功"}
rescue Exception => e rescue Exception => e
tip_exception("帖子创建失败,原因:#{e}") tip_exception("帖子创建失败,原因:#{e}")
raise ActiveRecord::Rollback raise ActiveRecord::Rollback
@ -132,7 +132,7 @@ class MemosController < ApplicationController
def hidden def hidden
tip_exception("不能对主贴进行隐藏操作") if @memo.parent_id.nil? tip_exception("不能对主贴进行隐藏操作") if @memo.parent_id.nil?
begin begin
@memo.update_attributes!(hidden: !@memo.hidden) @memo.update_attributes!(hidden: @memo.hidden == 0 ? 1 : 0)
normal_status("更新成功") normal_status("更新成功")
rescue Exception => e rescue Exception => e
tip_exception("更新失败,原因:#{e}") tip_exception("更新失败,原因:#{e}")
@ -147,16 +147,15 @@ class MemosController < ApplicationController
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
begin begin
memo = Memo.find_by!(id: params[:parent_id]) memo = Memo.find_by!(id: params[:parent_id])
reply = Memo.new @reply = Memo.new
reply.content = params[:content] @reply.content = params[:content]
reply.author = current_user @reply.author = current_user
reply.forum_id = memo.forum_id @reply.forum_id = memo.forum_id
reply.subject = memo.subject @reply.subject = memo.subject
reply.root_id = memo.root_id || memo.id @reply.root_id = memo.root_id || memo.id
memo.children << reply memo.children << @reply
m = Memo.find_by!(id: reply.root_id) m = Memo.find_by!(id: @reply.root_id)
m.increment!(:all_replies_count) m.increment!(:all_replies_count)
normal_status("回复成功")
rescue Exception => e rescue Exception => e
tip_exception("回复失败,原因:#{e}") tip_exception("回复失败,原因:#{e}")
raise ActiveRecord::Rollback raise ActiveRecord::Rollback
@ -183,10 +182,6 @@ class MemosController < ApplicationController
tip_exception(403, "无权限操作") unless @memo.author == current_user || current_user.admin? || current_user.business? tip_exception(403, "无权限操作") unless @memo.author == current_user || current_user.admin? || current_user.business?
end 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.require(:memo).permit(:subject, :content, :forum_id) params.require(:memo).permit(:subject, :content, :forum_id)
@ -196,7 +191,7 @@ class MemosController < ApplicationController
tip_exception("话题名称不能为空") if params[:subject].blank? tip_exception("话题名称不能为空") if params[:subject].blank?
tip_exception("话题内容不能为空") if params[:content].blank? tip_exception("话题内容不能为空") if params[:content].blank?
tip_exception("话题类型不能为空") if params[:forum_id].blank? tip_exception("话题类型不能为空") if params[:forum_id].blank?
tip_exception("技术标签不能为空") if params[:tags].blank? tip_exception("技术标签不能为空") if params[:forum_id].to_i == 5 && params[:tags].blank?
end end
end end

@ -265,6 +265,7 @@ class MyshixunsController < ApplicationController
# params[:evaluate] 实训评测时更新必须给的参数,需要依据该参数做性能统计,其它类型的更新可以跳过 # params[:evaluate] 实训评测时更新必须给的参数,需要依据该参数做性能统计,其它类型的更新可以跳过
# 自动保存的时候evaluate为0点评测的时候为1 # 自动保存的时候evaluate为0点评测的时候为1
if params[:evaluate] == 1 if params[:evaluate] == 1
#exec_time = game.challenge.try(:exec_time)
@sec_key = generate_identifier(EvaluateRecord, 12) @sec_key = generate_identifier(EvaluateRecord, 12)
record = EvaluateRecord.create!(:user_id => current_user.id, :shixun_id => @myshixun.shixun_id, :game_id => game_id, record = EvaluateRecord.create!(:user_id => current_user.id, :shixun_id => @myshixun.shixun_id, :game_id => game_id,
:identifier => @sec_key) :identifier => @sec_key)

@ -919,6 +919,7 @@ class PollsController < ApplicationController
:end_at => Time.now :end_at => Time.now
} }
poll_user_current.update_attributes(poll_user_params) poll_user_current.update_attributes(poll_user_params)
CommitPollNotifyJobJob.perform_later(@poll.id, current_user.id)
normal_status(0, "问卷提交成功!") normal_status(0, "问卷提交成功!")
end end
## 需添加发送消息的接口,稍后添加 ## 需添加发送消息的接口,稍后添加

@ -1,7 +1,7 @@
class ProjectPackagesController < ApplicationController class ProjectPackagesController < ApplicationController
include PaginateHelper include PaginateHelper
before_action :require_login, :check_auth, only: %i[create update destroy] before_action :require_login, :check_auth, only: %i[show create update destroy]
helper_method :current_package, :package_manageable? helper_method :current_package, :package_manageable?

@ -164,7 +164,7 @@ class ShixunsController < ApplicationController
@new_shixun = Shixun.new @new_shixun = Shixun.new
@new_shixun.attributes = @shixun.attributes.dup.except("id","user_id","visits","gpid","status", "identifier", "averge_star", @new_shixun.attributes = @shixun.attributes.dup.except("id","user_id","visits","gpid","status", "identifier", "averge_star",
"homepage_show","repo_name", "myshixuns_count", "challenges_count", "homepage_show","repo_name", "myshixuns_count", "challenges_count",
"can_copy") "can_copy", "created_at", "updated_at")
@new_shixun.user_id = User.current.id @new_shixun.user_id = User.current.id
@new_shixun.averge_star = 5 @new_shixun.averge_star = 5
@new_shixun.identifier = generate_identifier Shixun, 8 @new_shixun.identifier = generate_identifier Shixun, 8
@ -406,9 +406,10 @@ class ShixunsController < ApplicationController
end end
end end
@shixun.update_attributes(shixun_params) @shixun.update_attributes(shixun_params)
logger.info("##########shixun_info_params: #{shixun_info_params}")
logger.info("##########params[:shixun_info][:evaluate_script]: #{params[:shixun_info][:evaluate_script]}")
@shixun.shixun_info.update_attributes(shixun_info_params) @shixun.shixun_info.update_attributes(shixun_info_params)
@shixun.shixun_schools.delete_all @shixun.shixun_schools.delete_all
logger.info("##########scope_partment:###{params[:scope_partment]}")
# scope_partment: 高校的名称 # scope_partment: 高校的名称
if params[:scope_partment].present? if params[:scope_partment].present?
arr = [] arr = []

@ -1,6 +1,7 @@
class TidingsController < ApplicationController class TidingsController < ApplicationController
include PaginateHelper include PaginateHelper
before_action :require_login
after_action :update_onclick_time!, only: [:index] after_action :update_onclick_time!, only: [:index]
def index def index

@ -9,6 +9,8 @@ class Users::PrivateMessagesController < Users::BaseController
query = "SELECT subquery.*, COUNT(*) message_count FROM (#{subquery}) subquery "\ 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}" "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) @messages = PrivateMessage.select('*').from("(#{query}) AS query").includes(target: :user_extension)
observed_user.private_messages.only_unread.update_all(status: 1)
end end
def create def create

@ -1,5 +1,5 @@
module CourseDecorator module CourseDecorator
def can_visited? def can_visited?
is_public == 1 || User.current.admin? || User.current.member_of_course?(self) is_public == 1 || User.current.admin_or_business? || User.current.member_of_course?(self)
end end
end end

@ -2,6 +2,9 @@ module TidingDecorator
def content def content
method_name = "#{container_type.underscore}_content" method_name = "#{container_type.underscore}_content"
respond_to?(method_name) ? send(method_name) : '' respond_to?(method_name) ? send(method_name) : ''
rescue => ex
Util.logger_error(ex)
''
end end
def how_long_time def how_long_time
@ -27,6 +30,7 @@ module TidingDecorator
end end
def strip_html(text, len = 0, suffix = "...") def strip_html(text, len = 0, suffix = "...")
text = text.to_s
str = "" str = ""
if !text.nil? && text.length > 0 if !text.nil? && text.length > 0
str = text.gsub(/<\/?.*?>/, '').strip str = text.gsub(/<\/?.*?>/, '').strip
@ -43,13 +47,13 @@ module TidingDecorator
# ================ 各种类消息内容方法 ================ # ================ 各种类消息内容方法 ================
def apply_user_authentication_content def apply_user_authentication_content
return if trigger_user_id.zero? t_user = trigger_user || User.find(1)
if tiding_type == 'Apply' if tiding_type == 'Apply'
str1, str2 = if container.auth_type == 1 str1, str2 = if container.auth_type == 1
[trigger_user.show_real_name, trigger_user.ID_number] [t_user.show_real_name, t_user.ID_number]
elsif container.auth_type == 2 elsif container.auth_type == 2
ue = trigger_user.user_extension ue = t_user.user_extension
[[ue.school&.name, ue.department&.name].join('_'), ue.identity_text] [[ue.school&.name, ue.department&.name].join('_'), ue.identity_text]
end end
I18n.t(locale_format(tiding_type, container.auth_type)) % [str1, str2] I18n.t(locale_format(tiding_type, container.auth_type)) % [str1, str2]
@ -93,7 +97,7 @@ module TidingDecorator
elsif status == 2 elsif status == 2
I18n.t(locale_format(tiding_type, "#{status}_#{extra.nil?}"), reason: extra) % [name, second_name] I18n.t(locale_format(tiding_type, "#{status}_#{extra.nil?}"), reason: extra) % [name, second_name]
else else
I18n.t(locale_format(tiding_type, status)) % [name, second_name] I18n.t(locale_format(tiding_type, status), reason: extra) % [name, second_name]
end end
end end
@ -102,9 +106,9 @@ module TidingDecorator
if tiding_type == 'Apply' if tiding_type == 'Apply'
I18n.t(locale_format(tiding_type)) % name I18n.t(locale_format(tiding_type)) % name
elsif status == 2 elsif status == 2
I18n.t(locale_format(tiding_type, "#{status}_#{extra.nil?}"), reason: extra) % name I18n.t(locale_format(tiding_type, "#{status}_#{extra.nil?}"), name: name, reason: extra)
else else
I18n.t(locale_format(tiding_type, status)) % name I18n.t(locale_format(tiding_type, status), name: name, reason: extra)
end end
end end
@ -123,7 +127,11 @@ module TidingDecorator
end end
def course_content def course_content
I18n.t(locale_format) % container.name if tiding_type == 'Delete'
I18n.t(locale_format(tiding_type)) % extra
else
I18n.t(locale_format) % container.name
end
end end
def shixun_content def shixun_content
@ -261,16 +269,16 @@ module TidingDecorator
def journal_content def journal_content
case tiding_type case tiding_type
when 'Mentioned' then when 'Mentioned' then
I18n.t(locale_format(tiding_type)) % message_content_helper(container.notes) I18n.t(locale_format(tiding_type)) % message_content_helper(container&.notes)
when 'Comment' then when 'Comment' then
I18n.t(locale_format(tiding_type, container.parent.present?)) % message_content_helper(container.notes) I18n.t(locale_format(tiding_type, container.parent.present?)) % message_content_helper(container&.notes)
else else
I18n.t(locale_format) % container.issue.subject I18n.t(locale_format) % container&.issue&.subject
end end
end end
def issue_content def issue_content
I18n.t(locale_format) % container.subject I18n.t(locale_format) % container&.subject
end end
def pull_request_content def pull_request_content
@ -291,15 +299,15 @@ module TidingDecorator
end end
def poll_content def poll_content
I18n.t(locale_format(parent_container_type)) % container.polls_name I18n.t(locale_format(parent_container_type)) % container&.polls_name
end end
def exercise_content def exercise_content
I18n.t(locale_format(parent_container_type)) % container.exercise_name I18n.t(locale_format(parent_container_type)) % container&.exercise_name
end end
def student_graduation_topic_content def student_graduation_topic_content
I18n.t(locale_format) % container.graduation_topic.try(:name) I18n.t(locale_format) % container&.graduation_topic.try(:name)
end end
def deal_student_topic_select_content def deal_student_topic_select_content
@ -307,27 +315,27 @@ module TidingDecorator
end end
def graduation_task_content def graduation_task_content
I18n.t(locale_format(parent_container_type)) % container.name I18n.t(locale_format(parent_container_type)) % container&.name
end end
def graduation_work_content def graduation_work_content
I18n.t(locale_format(extra.nil?)) % container.graduation_task.try(:name) I18n.t(locale_format(extra.nil?)) % container&.graduation_task.try(:name)
end end
def graduation_work_score_content def graduation_work_score_content
I18n.t(locale_format) % container.graduation_work.graduation_task.try(:name) I18n.t(locale_format) % container&.graduation_work&.graduation_task.try(:name)
end end
def homework_common_content def homework_common_content
I18n.t(locale_format(parent_container_type), name: container.name, reason: extra) I18n.t(locale_format(parent_container_type), name: container&.name, reason: extra)
end end
def student_work_content def student_work_content
I18n.t(locale_format(extra.nil?)) % container.homework_common.try(:name) I18n.t(locale_format(extra.nil?)) % container&.homework_common.try(:name)
end end
def student_works_score_content def student_works_score_content
I18n.t(locale_format(extra)) % container.student_work.homework_common.try(:name) I18n.t(locale_format(extra)) % container&.student_work&.homework_common.try(:name)
end end
def challenge_work_score_content def challenge_work_score_content

@ -5,8 +5,8 @@ class Users::UpdateAccountForm
attr_accessor :nickname, :name, :show_realname, :gender, :location, :location_city, attr_accessor :nickname, :name, :show_realname, :gender, :location, :location_city,
:identity, :student_id, :technical_title, :school_id, :department_id :identity, :student_id, :technical_title, :school_id, :department_id
validates :nickname, presence: true validates :nickname, presence: true, length: { maximum: 20 }
validates :name, presence: true validates :name, presence: true, length: { maximum: 10 }
validates :gender, presence: true, numericality: { only_integer: true }, inclusion: { in: [0, 1] } validates :gender, presence: true, numericality: { only_integer: true }, inclusion: { in: [0, 1] }
validates :location, presence: true validates :location, presence: true
validates :location_city, presence: true validates :location_city, presence: true

@ -62,15 +62,18 @@ module ApplicationHelper
# shixun开启挑战对应的行为名及url # shixun开启挑战对应的行为名及url
def task_operation_url current_myshixun, shixun def task_operation_url current_myshixun, shixun
url = "/shixuns/#{shixun.identifier}/shixun_exec" if current_myshixun.blank?
name = name = shixun.status == 0 ? "模拟实战" : "开启挑战"
if current_myshixun.blank? url = "/shixuns/#{shixun.identifier}/shixun_exec"
shixun.status == 0 ? "模拟实战" : "开启挑战" else
elsif current_myshixun.status == 1 identifier = current_myshixun.current_task(current_myshixun.games).try(:identifier)
"查看实战" if current_myshixun.status == 1
name = "查看实战"
else else
"继续挑战" name = "继续挑战"
end end
url = identifier
end
[name, url] [name, url]
end end

@ -194,7 +194,7 @@ module CoursesHelper
# 获取课堂的资源数 # 获取课堂的资源数
def get_attachment_count(course, category_id) def get_attachment_count(course, category_id)
course.attachments.where(course_second_category_id: category_id).size category_id.to_i == 0 ? course.attachments.size : course.attachments.where(course_second_category_id: category_id).size
end end
# 获取课堂的作业数 # 获取课堂的作业数

@ -0,0 +1,26 @@
class CommitExercsieNotifyJobJob < ApplicationJob
queue_as :notify
def perform(exercise_id, user_id)
exercise = Exercise.find_by(id: exercise_id)
user = User.find_by(id: user_id)
return if [exercise, user].any?(&:blank?)
course = exercise.course
attrs = %i[user_id trigger_user_id container_id container_type parent_container_id parent_container_type
belong_container_id belong_container_type tiding_type viewed status created_at updated_at]
same_attrs = {
trigger_user_id: user.id,
container_id: exercise.id, container_type: 'Exercise',
parent_container_id: exercise.id, parent_container_type: 'CommitExercise',
belong_container_id: course.id, belong_container_type: 'Course',
tiding_type: 'Exercise', viewed: 0, status: 0
}
Tiding.bulk_insert(*attrs) do |worker|
course.course_member(user).member_teachers.each do |teacher|
worker.add same_attrs.merge(user_id: teacher.user_id)
end
end
end
end

@ -0,0 +1,26 @@
class CommitPollNotifyJobJob < ApplicationJob
queue_as :notify
def perform(poll_id, user_id)
poll = Poll.find_by(id: poll_id)
user = User.find_by(id: user_id)
return if [poll, user].any?(&:blank?)
course = poll.course
attrs = %i[user_id trigger_user_id container_id container_type parent_container_id parent_container_type
belong_container_id belong_container_type tiding_type viewed status created_at updated_at]
same_attrs = {
trigger_user_id: user.id,
container_id: poll.id, container_type: 'Poll',
parent_container_id: poll.id, parent_container_type: 'CommitPoll',
belong_container_id: course.id, belong_container_type: 'Course',
tiding_type: 'Poll', viewed: 0, status: 0
}
Tiding.bulk_insert(*attrs) do |worker|
course.course_member(user).member_teachers.each do |teacher|
worker.add same_attrs.merge(user_id: teacher.user_id)
end
end
end
end

@ -9,6 +9,7 @@ class ApplyAddSchool < ApplicationRecord
private private
def send_notify def send_notify
tidings.create!(user_id: 1, status: 0, trigger_user_id: user_id, belong_container: school, tiding_type: 'Apply') Tiding.create!(user_id: 1, status: 0, container_id: id, container_type: 'ApplyAddSchools',
trigger_user_id: user_id, belong_container: school, tiding_type: 'Apply')
end end
end end

@ -3,8 +3,18 @@
class ApplyUserAuthentication < ApplicationRecord class ApplyUserAuthentication < ApplicationRecord
belongs_to :user belongs_to :user
has_many :tidings, :as => :container, :dependent => :destroy
scope :real_name_auth, -> { where(auth_type: 1) } scope :real_name_auth, -> { where(auth_type: 1) }
scope :professional_auth, -> { where(auth_type: 2) } scope :professional_auth, -> { where(auth_type: 2) }
scope :processing, -> { where(status: 0) } scope :processing, -> { where(status: 0) }
scope :passed, -> { where(status: 1) } scope :passed, -> { where(status: 1) }
after_create :send_tiding
private
def send_tiding
self.tidings << Tiding.new(:user_id => '1', :status=> 0, :trigger_user_id => user_id, :belong_container_id => 1, :belong_container_type =>'User', :tiding_type => "Apply")
end
end end

@ -11,7 +11,7 @@ class CourseMessage < ApplicationRecord
def pass! def pass!
update!(status: :PASSED) update!(status: :PASSED)
send_deal_tiding send_deal_tiding(1)
end end
def application_user def application_user
@ -20,16 +20,16 @@ class CourseMessage < ApplicationRecord
def reject! def reject!
update!(status: :REJECTED) update!(status: :REJECTED)
send_deal_tiding send_deal_tiding(2)
end end
private private
def send_deal_tiding def send_deal_tiding deal_status
# 发送申请处理结果消息 # 发送申请处理结果消息
Tiding.create!( Tiding.create!(
user_id: user_id, trigger_user: User.current, container_id: course_id, container_type: 'DealCourse', user_id: course_message_id, trigger_user: User.current, container_id: course_id, container_type: 'DealCourse',
belong_container: course, extra: content.to_i == 2 ? '7' : '9', tiding_type: 'System', status: status == :PASSED ? 1 : 2 belong_container: course, extra: content.to_i == 2 ? '9' : '7', tiding_type: 'System', status: deal_status
) )
# 将申请消息置为已处理 # 将申请消息置为已处理
Tiding.where(trigger_user_id: user_id, container_id: course_id, container_type: 'JoinCourse', status: 0).update_all(status: 1) Tiding.where(trigger_user_id: user_id, container_id: course_id, container_type: 'JoinCourse', status: 0).update_all(status: 1)

@ -11,6 +11,7 @@ class JournalsForMessage < ApplicationRecord
scope :parent_comment, -> { where(m_parent_id: nil)} scope :parent_comment, -> { where(m_parent_id: nil)}
scope :search_by_jour_type, lambda{|type,ids| where(jour_type:type,jour_id: ids)} scope :search_by_jour_type, lambda{|type,ids| where(jour_type:type,jour_id: ids)}
has_many :tidings, as: :container, dependent: :destroy
# "jour_type", # 留言所属类型 # "jour_type", # 留言所属类型
# "jour_id", # 留言所属类型的id # "jour_id", # 留言所属类型的id
@ -25,6 +26,8 @@ class JournalsForMessage < ApplicationRecord
# "is_comprehensive_evaluation", # 1 教师评论、2 匿评、3 留言 # "is_comprehensive_evaluation", # 1 教师评论、2 匿评、3 留言
# "hidden", 隐藏 # "hidden", 隐藏
after_create :send_tiding
# course_identity 课堂用户身份 # course_identity 课堂用户身份
def contents_show course_identity def contents_show course_identity
@ -47,4 +50,29 @@ class JournalsForMessage < ApplicationRecord
JournalsForMessage.includes(:user).where(m_parent_id: self.id).page(page).per(limit).reorder("created_on asc") JournalsForMessage.includes(:user).where(m_parent_id: self.id).page(page).per(limit).reorder("created_on asc")
end end
def send_tiding
# 回复和@同一个人时:只发@的消息(因@的消息先创建)
case self.jour_type
# 用户留言当做私信处理 不发消息
when "Principal"
=begin
user_id = self.m_parent_id.present? ? JournalsForMessage.find(self.m_parent_id).user_id : self.jour_id
if user_id != self.user_id && !self.tidings.where(:user_id => user_id, :trigger_user_id => self.user_id, :tiding_type => "Mentioned").first.present?
self.tidings << Tiding.new(:trigger_user_id => self.user_id, :user_id => user_id, :parent_container_id => self.jour_id, :parent_container_type => self.jour_type, :belong_container_id => self.jour_id, :belong_container_type => "User", :viewed => 0, :tiding_type => self.m_parent_id.present? ? "Comment" : "Journal")
end
=end
when "HomeworkCommon", "GraduationTopic"
user_id = self.m_parent_id.present? ? JournalsForMessage.find(self.m_parent_id).user_id : (self.jour_type == "HomeworkCommon" ? self.jour.user_id : self.jour.tea_id)
if user_id != self.user_id && !self.tidings.where(:user_id => user_id, :trigger_user_id => self.user_id, :tiding_type => "Mentioned").first.present?
self.tidings << Tiding.new(:trigger_user_id => self.user_id, :user_id => user_id, :parent_container_id => self.jour_id, :parent_container_type => self.jour_type, :belong_container_id => self.jour.course_id, :belong_container_type => "Course", :viewed => 0, :tiding_type => "Comment")
end
when "StudentWorksScore"
course_id = self.jour.try(:student_work).try(:homework_common).try(:course_id)
user_id = self.m_parent_id.present? ? JournalsForMessage.find(self.m_parent_id).user_id : self.jour.user_id
if user_id != self.user_id && !self.tidings.where(:user_id => user_id, :trigger_user_id => self.user_id, :tiding_type => "Mentioned").first.present?
self.tidings << Tiding.new(:trigger_user_id => self.user_id, :user_id => user_id, :parent_container_id => self.jour_id, :parent_container_type => self.jour_type, :belong_container_id => course_id, :belong_container_type => "Course", :viewed => 0, :tiding_type => "Comment")
end
end
end
end end

@ -10,6 +10,8 @@ class Library < ApplicationRecord
has_many :attachments, as: :container has_many :attachments, as: :container
has_one :praise_tread_cache, foreign_key: :object_id has_one :praise_tread_cache, foreign_key: :object_id
has_many :praise_treads, as: :praise_tread_object, dependent: :destroy
validates :uuid, presence: true, uniqueness: true validates :uuid, presence: true, uniqueness: true

@ -38,6 +38,7 @@ class Shixun < ApplicationRecord
belongs_to :user belongs_to :user
# 实训服务配置 # 实训服务配置
has_many :shixun_service_configs, :dependent => :destroy has_many :shixun_service_configs, :dependent => :destroy
has_many :tidings, as: :container, dependent: :destroy
scope :search_by_name, ->(keyword) { where("name like ? or description like ? ", scope :search_by_name, ->(keyword) { where("name like ? or description like ? ",
"%#{keyword}%", "%#{keyword}%") } "%#{keyword}%", "%#{keyword}%") }
@ -62,6 +63,8 @@ class Shixun < ApplicationRecord
scope :field_for_recommend, lambda{ select([:id, :name, :identifier, :myshixuns_count]) } scope :field_for_recommend, lambda{ select([:id, :name, :identifier, :myshixuns_count]) }
scope :find_by_ids,lambda{|k| where(id:k)} scope :find_by_ids,lambda{|k| where(id:k)}
after_create :send_tiding
# REDO:  # REDO: 
def propaedeutics def propaedeutics
shixun_info.try(:propaedeutics) shixun_info.try(:propaedeutics)
@ -242,4 +245,11 @@ class Shixun < ApplicationRecord
def finished_challenges_count(user) def finished_challenges_count(user)
Game.joins(:myshixun).where(user_id: user.id, status: 2, myshixuns: { shixun_id: id }).count Game.joins(:myshixun).where(user_id: user.id, status: 2, myshixuns: { shixun_id: id }).count
end end
private
def send_tiding
self.tidings << Tiding.new(:user_id => user_id, :trigger_user_id => 1, :belong_container_id => id, :belong_container_type =>'Shixun', :tiding_type => "System", :viewed => 0)
end
end end

@ -17,7 +17,12 @@ class StudentGraduationTopic < ApplicationRecord
scope :is_refused, -> {where(status: 2)} scope :is_refused, -> {where(status: 2)}
scope :is_accepted, -> {where(status: 1)} scope :is_accepted, -> {where(status: 1)}
scope :is_accepting, -> {where(status: 0)} scope :is_accepting, -> {where(status: 0)}
after_create :send_tiding
def send_tiding
self.tidings << Tiding.new(:user_id => self.graduation_topic.tea_id, :trigger_user_id => self.user_id, :parent_container_id => self.graduation_topic_id, :parent_container_type => "GraduationTopic",
:belong_container_id => self.graduation_topic.course_id, :belong_container_type => "Course", :viewed => 0, :status => 0, :tiding_type => "GraduationTopic")
end
# 学生名称 # 学生名称
def name def name

@ -9,16 +9,15 @@ class Tiding < ApplicationRecord
def identifier def identifier
value = nil value = nil
if Object.const_defined?(container_type)
value = container.try(:identifier)
end
if value.blank? && parent_container_type && Object.const_defined?(parent_container_type) value = container.try(:identifier) rescue nil
value = parent_container_type.try(:identifier)
if value.blank? && parent_container_type
value = parent_container_type.try(:identifier) rescue nil
end end
if value.blank? && belong_container_type && Object.const_defined?(belong_container_type) if value.blank? && belong_container_type
value = belong_container.try(:identifier) value = belong_container.try(:identifier) rescue nil
end end
value value

@ -234,7 +234,7 @@ class User < ApplicationRecord
# 课堂的老师(创建者、老师、助教),不用考虑当前身份 # 课堂的老师(创建者、老师、助教),不用考虑当前身份
def teacher_of_course_non_active?(course) def teacher_of_course_non_active?(course)
course.course_members.exists?(user_id: id, role: [1,2,3]) || admin? || business? course.course_members.exists?(user_id: id, role: [1,2,3])
end end
# 是否是教师,课堂管理员或者超级管理员 # 是否是教师,课堂管理员或者超级管理员

@ -54,7 +54,7 @@ class ProjectPackages::SaveService < ApplicationService
raise Error, '验证码不能为空' if params[:code].blank? raise Error, '验证码不能为空' if params[:code].blank?
code = VerificationCode.where(phone: params[:contact_phone], code_type: 9, code: params[:code]).last code = VerificationCode.where(phone: params[:contact_phone], code_type: 9, code: params[:code]).last
raise Error, '无效的验证码' if code.blank? || !code.valid_code? raise Error, '无效的验证码' if code.blank? || !code.effective?
end end
def deal_attachments def deal_attachments

@ -1,14 +1,16 @@
class ProjectPackages::WinBiddingService < ApplicationService class ProjectPackages::WinBiddingService < ApplicationService
Error = Class.new(StandardError) Error = Class.new(StandardError)
attr_reader :package, :params attr_reader :package, :user, :params
def initialize(package, params) def initialize(package, user, params)
@package = package @package = package
@user = user
@params = params @params = params
end end
def call def call
raise Error, '没有权限' unless package.creator_id == user.id || user.admin_or_business?
raise Error, '竞标报名还未结束' unless package.bidding_end? raise Error, '竞标报名还未结束' unless package.bidding_end?
raise Error, '该状态下不能选择中标者' unless package.may_finish_bidding? raise Error, '该状态下不能选择中标者' unless package.may_finish_bidding?

@ -1 +1,2 @@
json.course_id @course.id json.course_id @course.id
json.first_category_url module_url(@course.none_hidden_course_modules.first, @course)

@ -4,7 +4,7 @@ json.courses @courses do |course|
json.avatar_url url_to_avatar(course.teacher) json.avatar_url url_to_avatar(course.teacher)
json.creator course.teacher.real_name json.creator course.teacher.real_name
json.school course.school&.name json.school course.school&.name
json.technical_title course.teacher.identity json.technical_title "" # course.teacher.identity
json.course_members_count course.course_members_count json.course_members_count course.course_members_count
json.tasks_count get_tasks_count course json.tasks_count get_tasks_count course
json.visits course.visits json.visits course.visits

@ -11,6 +11,6 @@ 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.select{|pt| pt.praise_or_tread == 1}.count json.praise_count message.praise_treads.select{|pt| pt.praise_or_tread == 1}.count
json.user_praise message.praise_treads.select{|pt| pt.praise_or_tread == 1 && user_id == current_user.id}.count json.user_praise message.praise_treads.select{|pt| pt.praise_or_tread == 1 && pt.user_id == current_user.id}.count
json.child_message_count message.m_reply_count json.child_message_count message.m_reply_count
end end

@ -5,12 +5,17 @@ json.libraries do
json.cover_url library.cover_id.present? ? download_url(library.cover) : nil json.cover_url library.cover_id.present? ? download_url(library.cover) : nil
json.praise_count library.praise_tread_cache&.praise_num || 0 json.praise_count library.praises_count
json.download_count @download_count_map.fetch(library.id, 0) json.download_count @download_count_map.fetch(library.id, 0)
json.published_at library.display_published_at json.published_at library.display_published_at
json.created_at library.display_created_at json.created_at library.display_created_at
json.tags library.library_tags.map(&:name) # 标签
json.tags do
json.array! library.library_tags.each do |tag|
json.extract! tag, :id, :name
end
end
end end
end end

@ -2,7 +2,7 @@ library = current_library
json.extract! library, :id, :uuid, :title, :content, :author_name, :author_school_name, :status, :visited_count json.extract! library, :id, :uuid, :title, :content, :author_name, :author_school_name, :status, :visited_count
json.praise_count library.praise_tread_cache&.praise_num || 0 json.praise_count library.praises_count
json.published_at library.display_published_at json.published_at library.display_published_at
json.created_at library.display_created_at json.created_at library.display_created_at
@ -10,6 +10,7 @@ json.created_at library.display_created_at
# 创建者 # 创建者
json.creator do json.creator do
json.partial! 'users/user_simple', user: library.user json.partial! 'users/user_simple', user: library.user
json.school_name library.user.school_name
end end
# 封面 # 封面
@ -37,7 +38,7 @@ json.operation do
json.can_deletable manageable json.can_deletable manageable
json.can_editable manageable json.can_editable manageable
json.user_praised PraiseTread.exists?(user_id: current_user&.id) json.user_praised library.praise_treads.exists?(user_id: current_user&.id)
else else
json.can_deletable false json.can_deletable false
json.can_editable false json.can_editable false

@ -9,6 +9,6 @@ json.memo do
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.user_praise memo.praise_treads.user_liker(@user.try(:id)) ? true : false json.user_praise memo.praise_treads.user_liker(@user.try(:id)).count > 0 ? true : false
json.memo_praise_count memo.praise_treads.liker.count json.memo_praise_count memo.praise_treads.liker.count
end end

@ -0,0 +1,2 @@
json.(@reply, :id, :subject, :content, :hidden, :forum_id, :author_id, :all_replies_count, :is_md, :parent_id, :root_id,
:reward, :sticky, :updated_at, :created_at, :viewed_count)

@ -1,6 +1,19 @@
json.extract! tiding, :id, :status, :viewed, :user_id, :tiding_type, :container_id, :container_type, :parent_container_id, :parent_container_type json.extract! tiding, :id, :status, :viewed, :user_id, :tiding_type, :container_id, :container_type,
:parent_container_id, :parent_container_type, :belong_container_id, :belong_container_type
json.content tiding.content json.content tiding.content
json.identifier tiding.identifier json.identifier tiding.identifier
json.auth_type tiding.container_type == 'ApplyUserAuthentication' ? tiding.container.auth_type : nil
homework_type = nil
if tiding.container_type == 'HomeworkCommon'
homework_type = tiding.container.homework_type rescue nil
end
if homework_type.blank? && tiding.parent_container_type == 'HomeworkCommon'
homework_type = tiding.parent_container.homework_type rescue nil
end
json.homework_type homework_type
json.time tiding.how_long_time json.time tiding.how_long_time
json.new_tiding tiding.unread?(@onclick_time) json.new_tiding tiding.unread?(@onclick_time)

@ -3,6 +3,7 @@ json.message 'success'
json.private_message do json.private_message do
json.extract! @message, :id, :user_id, :receiver_id, :sender_id, :content json.extract! @message, :id, :user_id, :receiver_id, :sender_id, :content
json.send_day @message.send_time.strftime('%Y-%m-%d')
json.send_time @message.display_send_time json.send_time @message.display_send_time
json.sender do json.sender do
json.partial! 'users/user_simple', user: @message.sender json.partial! 'users/user_simple', user: @message.sender

@ -16,5 +16,11 @@ json.project_packages do
json.deadline_at package.display_deadline_at json.deadline_at package.display_deadline_at
json.published_at package.display_published_at json.published_at package.display_published_at
json.operation do
can_manage = current_user&.id == observed_user.id || current_user&.admin_or_business?
json.can_edit can_manage && package.editable?
json.can_delete can_manage && package.deletable?
end
end end
end end

@ -6,9 +6,9 @@
"2_end": "申请职业认证:%s %s" "2_end": "申请职业认证:%s %s"
System: System:
"1_1_end": "你提交的实名认证申请,审核已通过" "1_1_end": "你提交的实名认证申请,审核已通过"
"1_2_end": "你提交的实名认证申请,审核未通过&nbsp;&nbsp;&nbsp;&nbsp;原因:%{reason}" "1_2_end": "你提交的实名认证申请,审核未通过<br/><span>原因:%{reason}</span>"
"2_1_end": "你提交的职业认证申请,审核已通过" "2_1_end": "你提交的职业认证申请,审核已通过"
"2_2_end": "你提交的职业认证申请,审核未通过&nbsp;&nbsp;&nbsp;&nbsp;原因:%{reason}" "2_2_end": "你提交的职业认证申请,审核未通过<br/><span>原因:%{reason}</span>"
CancelUserAuthentication_end: "取消了你的实名认证:%s %s" CancelUserAuthentication_end: "取消了你的实名认证:%s %s"
CancelUserProCertification_end: "取消了你的实名认证:%s %s" CancelUserProCertification_end: "取消了你的实名认证:%s %s"
JoinCourse: JoinCourse:
@ -21,6 +21,9 @@
"7_2_end": "你提交的加入课堂申请:%s助教, 审核未通过" "7_2_end": "你提交的加入课堂申请:%s助教, 审核未通过"
StudentJoinCourse_end: "加入了课堂:%s学生" StudentJoinCourse_end: "加入了课堂:%s学生"
TeacherJoinCourse: TeacherJoinCourse:
"2_end": "%s将你加入课堂%s教师"
"3_end": "%s将你加入课堂%s助教"
"4_end": "%s将你加入课堂%s学生"
"9_end": "%s将你加入课堂%s教师" "9_end": "%s将你加入课堂%s教师"
"7_end": "%s将你加入课堂%s助教" "7_end": "%s将你加入课堂%s助教"
"10_end": "%s将你加入课堂%s学生" "10_end": "%s将你加入课堂%s学生"
@ -28,35 +31,37 @@
Apply_end: "申请添加二级单位:%s%s" Apply_end: "申请添加二级单位:%s%s"
System: System:
"1_end": "你提交的添加二级单位申请:%s%s审核已通过" "1_end": "你提交的添加二级单位申请:%s%s审核已通过"
"2_false_end": "你提交的添加二级单位申请:%s%s审核未通过&nbsp;&nbsp;&nbsp;&nbsp;原因:%{reason}" "2_false_end": "你提交的添加二级单位申请:%s%s审核未通过<br/><span>原因:%{reason}</span>"
"2_true_end": "你提交的添加二级单位申请:%s%s审核未通过" "2_true_end": "你提交的添加二级单位申请:%s%s审核未通过"
"3_end": "你提交的添加二级单位申请:%s%s已被更改为%{reason}" "3_end": "你提交的添加二级单位申请:%s%s已被更改为%{reason}"
ApplyAddSchools: ApplyAddSchools:
Apply_end: "申请添加单位:%s" Apply_end: "申请添加单位:%s"
System: System:
"1_end": "你提交的添加单位申请:%{name},审核已通过" "1_end": "你提交的添加单位申请:%{name},审核已通过"
"2_reason_end": "你提交的添加单位申请:%{name},审核未通过&nbsp;&nbsp;&nbsp;&nbsp;原因:%{reason}" "2_false_end": "你提交的添加单位申请:%{name},审核未通过<br/><span>原因:%{reason}</span>"
"2_no_reason_end": "你提交的添加单位申请:%{name},审核未通过" "2_true_end": "你提交的添加单位申请:%{name},审核未通过"
"3_end": "你提交的添加单位申请:%{name},已被更改为:%{reason}" "3_end": "你提交的添加单位申请:%{name},已被更改为:%{reason}"
ApplyAction: ApplyAction:
ApplyShixun: ApplyShixun:
System: System:
"1_end": "你提交的实训发布申请:%{name},审核已通过" "1_end": "你提交的实训发布申请:%{name},审核已通过"
"2_end": "你提交的实训发布申请:%{name},审核未通过&nbsp;&nbsp;&nbsp;&nbsp;原因:%{reason}" "2_end": "你提交的实训发布申请:%{name},审核未通过<br/><span>原因:%{reason}</span>"
Apply_end: "申请发布实训:%{name}" Apply_end: "申请发布实训:%{name}"
ApplySubject: ApplySubject:
System: System:
"1_end": "你提交的实训课程发布申请:%{name},审核已通过" "1_end": "你提交的实训课程发布申请:%{name},审核已通过"
"2_end": "你提交的实训课程发布申请:%{name},审核未通过&nbsp;&nbsp;&nbsp;&nbsp;原因:%{reason}" "2_end": "你提交的实训课程发布申请:%{name},审核未通过<br/><span>原因:%{reason}</span>"
Apply_end: "申请发布实训课程:%{name}" Apply_end: "申请发布实训课程:%{name}"
TrialAuthorization: TrialAuthorization:
System: System:
"1_end": "你提交的试用授权申请,审核已通过" "1_end": "你提交的试用授权申请,审核已通过"
"2_end": "你提交的试用授权申请,审核未通过&nbsp;&nbsp;&nbsp;&nbsp;原因:%{reason}" "2_end": "你提交的试用授权申请,审核未通过<br/><span>原因:%{reason}</span>"
Apply_end: "提交了试用授权申请" Apply_end: "提交了试用授权申请"
Course_end: "成功创建了课堂:%s" Course_end: "你创建了课堂:%s"
Shixun_end: "成功创建了实训:%s" Course:
Subject_end: "成功创建了实训课程:%s" Delete_end: "你删除了课堂:%s"
Shixun_end: "你创建了实训:%s"
Subject_end: "你创建了实训课程:%s"
ArchiveCourse_end: "你的课堂已经归档:%s" ArchiveCourse_end: "你的课堂已经归档:%s"
JournalsForMessage: JournalsForMessage:
Mentioned_end: "@了你:%s" Mentioned_end: "@了你:%s"
@ -159,7 +164,7 @@
NearlyEnd_end: "试卷的截止时间快到啦:%s" NearlyEnd_end: "试卷的截止时间快到啦:%s"
CommitExercise_end: "提交了试卷答题:%s" CommitExercise_end: "提交了试卷答题:%s"
ExerciseScore_end: "评阅了你的试卷:%s" ExerciseScore_end: "评阅了你的试卷:%s"
StudentGraduationTopic_end: "选择毕设选题:%s" StudentGraduationTopic_end: "申请选择毕设选题:%s"
DealStudentTopicSelect: DealStudentTopicSelect:
1_end: "你提交的选题申请:%s审核已通过" 1_end: "你提交的选题申请:%s审核已通过"
2_end: "你提交的选题申请:%s审核未通过" 2_end: "你提交的选题申请:%s审核未通过"
@ -174,7 +179,7 @@
GraduationWorkScore_end: "评阅了你的作品:%s" GraduationWorkScore_end: "评阅了你的作品:%s"
HomeworkCommon: HomeworkCommon:
AnonymousComment_end: "开启了作业匿评:%{name}" AnonymousComment_end: "开启了作业匿评:%{name}"
AnonymousCommentFail_end: "开启作业匿评失败:%{name}<br/>原因:%{reason}" AnonymousCommentFail_end: "开启作业匿评失败:%{name}<br/><span>原因:%{reason}</span>"
AnonymousAppeal_end: "开启了匿评申诉:%{name}" AnonymousAppeal_end: "开启了匿评申诉:%{name}"
HomeworkPublish_end: "发布了作业:%{name}" HomeworkPublish_end: "发布了作业:%{name}"
NearlyEnd_end: "作业的提交截止时间快到啦:%{name}" NearlyEnd_end: "作业的提交截止时间快到啦:%{name}"
@ -190,11 +195,11 @@
ChallengeWorkScore_end: "调整了你的作品分数:%s" ChallengeWorkScore_end: "调整了你的作品分数:%s"
StudentWorksScoresAppeal: StudentWorksScoresAppeal:
UserAppealResult: UserAppealResult:
1_end: "同意了你提交的匿评申诉申请:%s" 1_end: "你提交的匿评申诉申请:%s,审核已通过"
2_end: "拒绝了你提交的匿评申诉:%s" 2_end: "你提交的匿评申诉:%s,审核未通过"
AppealResult: AppealResult:
1_end: "同意了他人对你的匿评申诉申请:%s" 1_end: "别人对你的匿评发起的申诉申请:%s审核已通过"
2_end: "拒绝了他人对你的匿评申诉:%s" 2_end: "别人对你的匿评发起的申诉申请:%s审核未通过"
StudentWork: StudentWork:
Apply_end: "发起了匿评申诉申请:%s" Apply_end: "发起了匿评申诉申请:%s"
HomeworkCommon_end: "有人对你的匿评发起了申诉:%s" HomeworkCommon_end: "有人对你的匿评发起了申诉:%s"
@ -203,12 +208,12 @@
Apply_end: "申请发布教学案例:%s" Apply_end: "申请发布教学案例:%s"
System: System:
1_end: "你提交的发布教学案例申请:%s审核已通过" 1_end: "你提交的发布教学案例申请:%s审核已通过"
2_end: "你提交的发布教学案例申请:%s审核未通过,原因:%{reason}" 2_end: "你提交的发布教学案例申请:%s审核未通过<br/><span>原因:%{reason}</span>"
ProjectPackage: ProjectPackage:
Apply_end: "申请发布众包需求:%s" Apply_end: "申请发布众包需求:%s"
System: System:
1_end: "你提交的众包需求申请:%s审核已通过" 1_end: "你提交的众包需求申请:%s审核已通过"
2_end: "你提交的众包需求申请:%s审核未通过,原因:%{reason}" 2_end: "你提交的众包需求申请:%s审核未通过<br/><span>原因:%{reason}</span>"
Created_end: "你创建了众包需求:%s" Created_end: "你创建了众包需求:%s"
Destroyed_end: "你删除了众包需求:%s" Destroyed_end: "你删除了众包需求:%s"
Bidding_end: "应征了你发布的众包任务:%s" Bidding_end: "应征了你发布的众包任务:%s"

@ -5,6 +5,7 @@ Rails.application.routes.draw do
mount Sidekiq::Web => '/sidekiq', :constraints => AdminConstraint.new mount Sidekiq::Web => '/sidekiq', :constraints => AdminConstraint.new
get 'attachments/download/:id', to: 'attachments#show' get 'attachments/download/:id', to: 'attachments#show'
get 'attachments/download/:id/:filename', to: 'attachments#show'
resources :edu_settings resources :edu_settings
scope '/api' do scope '/api' do

@ -1,7 +0,0 @@
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

@ -0,0 +1,12 @@
class AddPraisesCountToMemos < ActiveRecord::Migration[5.2]
def change
add_column :memos, :praises_count, :integer, :default => 0
memos = Memo.includes(:praise_treads).all
memos.find_each do |m|
puts("####{m.id}")
praises_count = m.praise_treads.select{|pt| pt.praise_or_tread == 1}.count
m.update_column(:praises_count, praises_count)
end
end
end

@ -0,0 +1,10 @@
class AddPraisesCountToLibraries < ActiveRecord::Migration[5.2]
def change
# add_column :libraries, :praises_count, :integer, :default => 0
#
# Library.find_each do |library|
# praises_count = library.praise_treads.count
# library.update_column(:praises_count, praises_count)
# end
end
end

@ -0,0 +1,5 @@
class TranferTidingData < ActiveRecord::Migration[5.2]
def change
Tiding.where(container_type: 'ApplyAddSchool').update_all(container_type: 'ApplyAddSchools')
end
end

@ -0,0 +1,7 @@
class SyncIndexToUsers < 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

Binary file not shown.

File diff suppressed because one or more lines are too long

@ -72,6 +72,7 @@
"react-infinite-scroller": "^1.2.4", "react-infinite-scroller": "^1.2.4",
"react-loadable": "^5.3.1", "react-loadable": "^5.3.1",
"react-monaco-editor": "^0.25.1", "react-monaco-editor": "^0.25.1",
"react-player": "^1.11.1",
"react-redux": "5.0.7", "react-redux": "5.0.7",
"react-router": "^4.2.0", "react-router": "^4.2.0",
"react-router-dom": "^4.2.2", "react-router-dom": "^4.2.2",

@ -577,7 +577,7 @@ p .activity-item:first-child{border-top: 1px solid #eee;}
.recently_name{float: left;line-height: 48px;display: block} .recently_name{float: left;line-height: 48px;display: block}
.recently_item:hover{background-color: #F9F9F9;} .recently_item:hover{background-color: #F9F9F9;}
/*私信对话框*/ /*私信对话框*/
.private-list{min-height: 660px;max-height: 810px;overflow-y: auto} .private-list{min-height: 660px;max-height: 831px;overflow-y: auto;overflow-x: hidden;}
.private-list .private-part{padding-left:20px;cursor: pointer} .private-list .private-part{padding-left:20px;cursor: pointer}
.private-part:hover{background-color: #F5F5F5;} .private-part:hover{background-color: #F5F5F5;}
.private-part.active{background-color: #F5F5F5;} .private-part.active{background-color: #F5F5F5;}

@ -209,6 +209,7 @@ $(function(){
// window resize // window resize
$(window).on('resize', function() { $(window).on('resize', function() {
window._tpiWidthResize && window._tpiWidthResize()
$('#games_repository_contents .CodeMirror.cm-s-railscasts').css("height", $("#games_repository_contents").height() - repositoryTabHeight); $('#games_repository_contents .CodeMirror.cm-s-railscasts').css("height", $("#games_repository_contents").height() - repositoryTabHeight);
}) })
@ -1221,6 +1222,7 @@ $(function(){ // 这里重新加一次事件监听,不在原有事件的基
); );
doc.live("mousemove", function(e) { doc.live("mousemove", function(e) {
if (dragging === true && lab == dragDom) { if (dragging === true && lab == dragDom) {
window._tpiWidthResize && window._tpiWidthResize()
// React 组件中需要resize搜索该引用可以找到初始化的位置 // React 组件中需要resize搜索该引用可以找到初始化的位置
window._currentChildcommentMDEditor && window._currentChildcommentMDEditor.resize() window._currentChildcommentMDEditor && window._currentChildcommentMDEditor.resize()
} }

@ -1,57 +1,58 @@
.App { .App {
text-align: center; text-align: center;
} }
.App-logo { .App-logo {
animation: App-logo-spin infinite 20s linear; animation: App-logo-spin infinite 20s linear;
height: 80px; height: 80px;
} }
.App-header { .App-header {
background-color: #222; background-color: #222;
height: 150px; height: 150px;
padding: 20px; padding: 20px;
color: white; color: white;
} }
.App-title { .App-title {
font-size: 1.5em; font-size: 1.5em;
} }
.App-intro { .App-intro {
font-size: large; font-size: large;
} }
@keyframes App-logo-spin { @keyframes App-logo-spin {
from { transform: rotate(0deg); } from { transform: rotate(0deg); }
to { transform: rotate(360deg); } to { transform: rotate(360deg); }
} }
/* md /* md
codermirror maybeUpdateLineNumberWidth codermirror maybeUpdateLineNumberWidth
*/ */
.editormd .CodeMirror-linenumbers { .editormd .CodeMirror-linenumbers {
padding: 0; padding: 0;
} }
.editormd-html-preview hr, .editormd-preview-container hr { .editormd-html-preview hr, .editormd-preview-container hr {
/* 颜色加深 */ /* 颜色加深 */
border-top: 1px solid #ccc; border-top: 1px solid #ccc;
} }
/* 重置掉antd的一些样式 */ /* 重置掉antd的一些样式 */
html, body { html, body {
-webkit-font-smoothing: auto !important; -webkit-font-smoothing: auto !important;
} }
.ant-progress-textyes { .ant-progress-textyes {
color: #52c41a; color: #52c41a;
} }
.ant-progress-textno{ .ant-progress-textno{
color: #f5222d; color: #f5222d;
} }
/* md多空格 */ /* md多空格 */
.markdown-body p { .markdown-body p {
white-space: pre-wrap; white-space: pre-wrap;
margin-bottom: 0px !important;
} }

@ -18,9 +18,7 @@ import Notcompletedysl from './modules/user/Notcompletedysl';
import Trialapplicationysl from './modules/login/Trialapplicationysl'; import Trialapplicationysl from './modules/login/Trialapplicationysl';
import Trialapplicationreview from './modules/user/Trialapplicationreview'; import Trialapplicationreview from './modules/user/Trialapplicationreview';
import Addcourses from "./modules/courses/coursesPublic/Addcourses"; import Addcourses from "./modules/courses/coursesPublic/Addcourses";
import AccountProfile from"./modules/user/AccountProfile"; import AccountProfile from "./modules/user/AccountProfile";
import Trialapplication from './modules/login/Trialapplication' import Trialapplication from './modules/login/Trialapplication'
import NotFoundPage from './NotFoundPage' import NotFoundPage from './NotFoundPage'
@ -238,6 +236,10 @@ const ProjectPackages=Loadable({
loading: Loading, loading: Loading,
}) })
const Messagerouting= Loadable({
loader: () => import('./modules/message/js/Messagerouting'),
loading: Loading,
})
class App extends Component { class App extends Component {
constructor(props) { constructor(props) {
super(props) super(props)
@ -355,22 +357,37 @@ class App extends Component {
{/*课堂*/} {/*课堂*/}
<Route path="/courses" component={CoursesIndex} {...this.props}></Route> <Route path="/courses" component={CoursesIndex} {...this.props}></Route>
<<<<<<< HEAD
{/* <Route path="/forums" component={ForumsIndexComponent}> {/* <Route path="/forums" component={ForumsIndexComponent}>
</Route> */} </Route> */}
{/* 教学案例 */} {/* 教学案例 */}
<Route path="/moop_cases"render={ <Route path="/moop_cases"render={
(props) => (<MoopCases {...this.props} {...props} {...this.state} />) (props) => (<MoopCases {...this.props} {...props} {...this.state} />)
}/> }/>
=======
<Route path="/forums"
render={
(props)=>(<ForumsIndexComponent {...this.props} {...props} {...this.state}></ForumsIndexComponent>)
}
>
</Route>
>>>>>>> dev_aliyun
<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}/>
<Route path="/testCodeMirror" component={TestCodeMirrorComponent}/> <Route path="/testCodeMirror" component={TestCodeMirrorComponent}/>
<Route path="/testRCComponent" component={TestComponent}/> <Route path="/testRCComponent" component={TestComponent}/>
<Route path="/testUrlQuery" component={TestUrlQueryComponent}/> <Route path="/testUrlQuery" component={TestUrlQueryComponent}/>
<Route path="/messages"
render={
(props)=>(<Messagerouting {...this.props} {...props} {...this.state}></Messagerouting>)
}
></Route>
<Route exact path="/" component={ShixunsHome}/> <Route exact path="/" component={ShixunsHome}/>
<Route component={Shixunnopage}/> <Route component={Shixunnopage}/>
</Switch> </Switch>
</Router> </Router>

@ -43,7 +43,6 @@ export function initAxiosInterceptors(props) {
// 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参考来控制
const requestMap = {}; const requestMap = {};

@ -59,4 +59,6 @@ export function toPath(path) {
} }
// export default queryString export function getTaskUrlById(id) {
return `/tasks/${id}`
}

@ -1,5 +1,6 @@
import React,{ Component } from "react"; import React,{ Component } from "react";
import { getUrl2 } from "educoder"; import { getUrl2 } from "educoder";
import ReactPlayer from 'react-player'
const $ = window.$ const $ = window.$
let _url_origin = getUrl2() let _url_origin = getUrl2()
@ -10,33 +11,54 @@ class Clappr extends Component{
this.state={ this.state={
} }
} }
componentWillUnmount() {
this['player'+this.props.id] && this['player'+this.props.id].destroy()
}
componentDidMount() { componentDidMount() {
return;
const source = this.props.source || "http://your.video/here.mp4" const source = this.props.source || "http://your.video/here.mp4"
const { id } = this.props const { id, type } = this.props
const _id = `#_player${id}` const _id = `#_player${id}`
if (window['Clappr']) {
const player = new window.Clappr.Player({ if (!window['Clappr'] && window['ClapprLoading'] == true) {
setTimeout(() => {
this.componentDidMount()
}, 300)
return;
}
// && window['clappr-playback-rate-plugin']
if (window['Clappr'] ) {
// https://github.com/clappr/clappr/issues/1839
// http://clappr.github.io/classes/Player.html#method_mute
this['player'+id] = new window.Clappr.Player({
source: source, parentId: _id, source: source, parentId: _id,
plugins: { height: type == 'mp3' ? 60 : 360,
'core': [window.Clappr.MediaControl, window.Clappr.Playback] hideMediaControl: type == 'mp3' ? false : true,
} // plugins: {
// 'core': [window.Clappr.MediaControl, window['clappr-playback-rate-plugin'].default]
// }
}); });
} else { } else {
window['ClapprLoading'] = true;
$.getScript( $.getScript(
`${_url_origin}/javascripts/media/clappr.min.js`, `${_url_origin}/javascripts/media/clappr.min.js`,
(data, textStatus, jqxhr) => { (data, textStatus, jqxhr) => {
window.clappr = window.Clappr window.clappr = window.Clappr
$.getScript( // $.getScript(
`${_url_origin}/javascripts/media/clappr-playback-rate-plugin.min.js`, // `${_url_origin}/javascripts/media/clappr-playback-rate-plugin.min.js`,
(data, textStatus, jqxhr) => { // (data, textStatus, jqxhr) => {
const player = new window.Clappr.Player({ this['player'+id] = new window.Clappr.Player({
source: source, parentId: _id, source: source, parentId: _id,
plugins: { height: type == 'mp3' ? 60 : 360,
'core': [window.Clappr.MediaControl, window['clappr-playback-rate-plugin'].default] hideMediaControl: type == 'mp3' ? false : true,
} // plugins: {
// 'core': [window.Clappr.MediaControl, window['clappr-playback-rate-plugin'].default]
// }
}); });
}) // })
}); });
@ -62,16 +84,25 @@ class Clappr extends Component{
render(){ render(){
let { source, id, className } = this.props; let { source, id, className, type } = this.props;
const _id = `_player${id}` const _id = `_player${id}`
return( return(
<React.Fragment> <React.Fragment>
<style>{` {/* https://github.com/CookPete/react-player/issues/686 */}
<ReactPlayer url={source} playing={false} controls={true} width={400} height={ type == 'mp3' ? 55 : 290}/>
{/* <style>{`
.playback_rate { .playback_rate {
margin-right: 16px; margin-right: 16px;
} }
`}</style> `}</style>
<div id={_id} className={className}></div> <div id={_id} className={className + ' ' + type}></div> */}
{/* 原生 */}
{/* { type == 'mp3' ? <audio src={source} preload controls></audio>
: <video src={source} controls="controls">
您的浏览器不支持 video 标签
</video>} */}
</React.Fragment> </React.Fragment>
) )
} }

@ -3,7 +3,8 @@ import { from } from '_array-flatten@2.1.2@array-flatten';
// export { default as OrderStateUtil } from '../routes/Order/components/OrderStateUtil'; // export { default as OrderStateUtil } from '../routes/Order/components/OrderStateUtil';
export { getImageUrl as getImageUrl, getUrl as getUrl, getUrl2 as getUrl2, setImagesUrl as setImagesUrl export { getImageUrl as getImageUrl, getUrl as getUrl, getUrl2 as getUrl2, setImagesUrl as setImagesUrl
, getUploadActionUrl as getUploadActionUrl, getUploadActionUrlOfAuth as getUploadActionUrlOfAuth } from './UrlTool'; , getUploadActionUrl as getUploadActionUrl, getUploadActionUrlOfAuth as getUploadActionUrlOfAuth
, getTaskUrlById as getTaskUrlById } from './UrlTool';
export { default as queryString } from './UrlTool2'; export { default as queryString } from './UrlTool2';
export { SnackbarHOC as SnackbarHOC } from './SnackbarHOC'; export { SnackbarHOC as SnackbarHOC } from './SnackbarHOC';

@ -18,7 +18,7 @@ class EffectDisplayContent extends Component {
.effectDisplay .content>div { .effectDisplay .content>div {
flex: 1 flex: 1
} }
.effectDisplay .clappr { .effectDisplay .clappr, .effectDisplay .contentWrap {
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
@ -32,13 +32,13 @@ class EffectDisplayContent extends Component {
{content3 && <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>
<div className="clearfix df content" > <div className="clearfix df content" >
{content1 && <div className="fl mr03precent pt10 mb50"> {content1 && <div className="fl mr03precent pt10 mb50 contentWrap">
{content1} {content1}
</div>} </div>}
{content2 && <div className="fl mr03precent pt10 mb50"> {content2 && <div className="fl mr03precent pt10 mb50 contentWrap">
{content2} {content2}
</div>} </div>}
{content3 && <div className="fl mr03precent pt10 mb50"> {content3 && <div className="fl mr03precent pt10 mb50 contentWrap">
{content3} {content3}
</div>} </div>}
</div> </div>

@ -1,4 +1,6 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Redirect } from 'react-router'; import { Redirect } from 'react-router';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
@ -29,7 +31,11 @@ class EvaluateSuccessEffectDisplay extends Component {
} }
} }
hidepicture = () => { hidepicture = () => {
window.$('#picture_display').hide(); const dom = document.getElementById('picture_display');
ReactDOM.unmountComponentAtNode(dom)
// window.$('#picture_display').hide();
window.$('.data-tip-right').hide()
} }
renderContent = () => { renderContent = () => {
// qrcode // qrcode
@ -107,11 +113,11 @@ class EvaluateSuccessEffectDisplay extends Component {
<EffectDisplayContent <EffectDisplayContent
typeName="音频" typeName="音频"
content1={ orignal_file[0] && orignal_file[0].file_url content1={ orignal_file[0] && orignal_file[0].file_url
? <Clappr source={orignal_file[0].file_url} id="1" className="clappr"></Clappr> : null } ? <Clappr source={orignal_file[0].file_url} id="1" className="clappr" type="mp3"></Clappr> : null }
content2={ user_file[0] && user_file[0].file_url content2={ user_file[0] && user_file[0].file_url
? <Clappr source={user_file[0].file_url} id="2" className="clappr"></Clappr> : null } ? <Clappr source={user_file[0].file_url} id="2" className="clappr" type="mp3"></Clappr> : null }
content3={ answer_file[0] && answer_file[0].file_url content3={ answer_file[0] && answer_file[0].file_url
? <Clappr source={answer_file[0].file_url} id="3" className="clappr"></Clappr> : null } ? <Clappr source={answer_file[0].file_url} id="3" className="clappr" type="mp3"></Clappr> : null }
></EffectDisplayContent> ></EffectDisplayContent>
) )
} else if (type == 'mp4') { } else if (type == 'mp4') {
@ -119,11 +125,11 @@ class EvaluateSuccessEffectDisplay extends Component {
<EffectDisplayContent <EffectDisplayContent
typeName="视频" typeName="视频"
content1={ orignal_file[0] && orignal_file[0].file_url content1={ orignal_file[0] && orignal_file[0].file_url
? <Clappr source={orignal_file[0].file_url} id="1" className="clappr"></Clappr> : null } ? <Clappr source={orignal_file[0].file_url} id="1" className="clappr" type="mp4"></Clappr> : null }
content2={ user_file[0] && user_file[0].file_url content2={ user_file[0] && user_file[0].file_url
? <Clappr source={user_file[0].file_url} id="2" className="clappr"></Clappr> : null } ? <Clappr source={user_file[0].file_url} id="2" className="clappr" type="mp4"></Clappr> : null }
content3={ answer_file[0] && answer_file[0].file_url content3={ answer_file[0] && answer_file[0].file_url
? <Clappr source={answer_file[0].file_url} id="3" className="clappr"></Clappr> : null } ? <Clappr source={answer_file[0].file_url} id="3" className="clappr" type="mp4"></Clappr> : null }
></EffectDisplayContent> ></EffectDisplayContent>
) )
} }

@ -950,7 +950,7 @@ class Fileslists extends Component{
<div className="alltask edu-back-white" <div className="alltask edu-back-white"
style={ style={
{ {
display: files===undefined?'block' :files.length===0? 'block' : 'none' display: files===undefined?'none' :files.length===0? 'block' : 'none'
} }
} }
> >

@ -1,449 +1,449 @@
import React,{ Component } from "react"; import React,{ Component } from "react";
import { import {
Form, Input, InputNumber, Switch, Radio, Form, Input, InputNumber, Switch, Radio,
Slider, Button, Upload, Icon, Rate, Checkbox, message, Slider, Button, Upload, Icon, Rate, Checkbox, message,
Row, Col, Select, Modal, Divider Row, Col, Select, Modal, Divider
} from 'antd'; } from 'antd';
import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor'; import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor';
import axios from 'axios' import axios from 'axios'
import './board.css' import './board.css'
import "../common/formCommon.css" import "../common/formCommon.css"
import AddDirModal from './AddDirModal' import AddDirModal from './AddDirModal'
import { RouteHOC } from './common.js' import { RouteHOC } from './common.js'
import CBreadcrumb from '../common/CBreadcrumb' import CBreadcrumb from '../common/CBreadcrumb'
import {getUploadActionUrl, bytesToSize, uploadNameSizeSeperator, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll} from 'educoder'; import {getUploadActionUrl, bytesToSize, uploadNameSizeSeperator, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll} from 'educoder';
const confirm = Modal.confirm; const confirm = Modal.confirm;
const $ = window.$ const $ = window.$
const { Option } = Select; const { Option } = Select;
// https://lanhuapp.com/web/#/item/project/board/detail?pid=a3bcd4b1-99ce-4e43-8ead-5b8b0a410807&project_id=a3bcd4b1-99ce-4e43-8ead-5b8b0a410807&image_id=71072679-b925-4824-aceb-4649535e3652 // https://lanhuapp.com/web/#/item/project/board/detail?pid=a3bcd4b1-99ce-4e43-8ead-5b8b0a410807&project_id=a3bcd4b1-99ce-4e43-8ead-5b8b0a410807&image_id=71072679-b925-4824-aceb-4649535e3652
class BoardsNew extends Component{ class BoardsNew extends Component{
constructor(props){ constructor(props){
super(props); super(props);
this.mdRef = React.createRef(); this.mdRef = React.createRef();
this.state = { this.state = {
fileList: [], fileList: [],
boards: [], boards: [],
title_num: 60 title_num: 60
} }
} }
addSuccess = () => { addSuccess = () => {
this.fetchBoards() this.fetchBoards()
} }
fetchBoards = () => { fetchBoards = () => {
const isEdit = this.isEdit const isEdit = this.isEdit
const boardId = this.props.match.params.boardId const boardId = this.props.match.params.boardId
const boardsUrl = `/courses/board_list.json?board_id=${boardId}` const boardsUrl = `/courses/board_list.json?board_id=${boardId}`
axios.get(boardsUrl, { }) axios.get(boardsUrl, { })
.then((response) => { .then((response) => {
if (response.data.status == 0) { if (response.data.status == 0) {
this.setState({ this.setState({
boards: response.data.data.boards || [], boards: response.data.data.boards || [],
course_id: response.data.data.course_id course_id: response.data.data.course_id
}) })
if (!isEdit) { if (!isEdit) {
response.data.data.boards.forEach( board => { response.data.data.boards.forEach( board => {
if (board.id == boardId) { if (board.id == boardId) {
this.setState({ board_name: board.name }) this.setState({ board_name: board.name })
} }
}) })
// board_name // board_name
} }
} }
}) })
.catch(function (error) { .catch(function (error) {
console.log(error); console.log(error);
}); });
} }
componentDidMount = () => { componentDidMount = () => {
const topicId = this.props.match.params.topicId const topicId = this.props.match.params.topicId
const isEdit = !!topicId const isEdit = !!topicId
this.isEdit = isEdit this.isEdit = isEdit
const boardId = this.props.match.params.boardId const boardId = this.props.match.params.boardId
this.fetchBoards() this.fetchBoards()
if (isEdit) { if (isEdit) {
const url = `/messages/${topicId}.json` const url = `/messages/${topicId}.json`
axios.get(url, { axios.get(url, {
}) })
.then((response) => { .then((response) => {
if (response.data.status == 0) { if (response.data.status == 0) {
const { id, data } = response.data; const { id, data } = response.data;
if (data) { if (data) {
this.editTopic = data; this.editTopic = data;
this.props.form.setFieldsValue({ this.props.form.setFieldsValue({
sticky: !!data.sticky, sticky: !!data.sticky,
content: data.content, content: data.content,
subject: data.subject, subject: data.subject,
select_board_id: data.board_id // TODO 没返回给前端 select_board_id: data.board_id // TODO 没返回给前端
}); });
this.mdRef.current.setValue(data.content) this.mdRef.current.setValue(data.content)
const _fileList = data.attachments.map(item => { const _fileList = data.attachments.map(item => {
return { return {
id: item.id, id: item.id,
uid: item.id, uid: item.id,
name: appendFileSizeToUploadFile(item), name: appendFileSizeToUploadFile(item),
url: item.url, url: item.url,
status: 'done' status: 'done'
} }
}) })
this.setState({ fileList: _fileList, board_name: data.board_name, title_num: 60 - parseInt(data.subject.length) }) this.setState({ fileList: _fileList, board_name: data.board_name, title_num: 60 - parseInt(data.subject.length) })
} }
} }
}) })
.catch(function (error) { .catch(function (error) {
console.log(error); console.log(error);
}); });
} else { } else {
const boardId = this.props.match.params.boardId const boardId = this.props.match.params.boardId
this.props.form.setFieldsValue({ this.props.form.setFieldsValue({
select_board_id: parseInt(boardId) select_board_id: parseInt(boardId)
}); });
} }
} }
handleSubmit = (e) => { handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
const cid = this.state.course_id const cid = this.state.course_id
const boardId = this.props.match.params.boardId const boardId = this.props.match.params.boardId
this.props.form.validateFieldsAndScroll((err, values) => { this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) { if (!err) {
console.log('Received values of form: ', values); console.log('Received values of form: ', values);
if (this.isEdit == true) { if (this.isEdit == true) {
const editTopic = this.editTopic const editTopic = this.editTopic
const editUrl = `/messages/${editTopic.id}.json` const editUrl = `/messages/${editTopic.id}.json`
let attachment_ids = undefined let attachment_ids = undefined
if (this.state.fileList) { if (this.state.fileList) {
attachment_ids = this.state.fileList.map(item => { attachment_ids = this.state.fileList.map(item => {
return item.response ? item.response.id : item.id return item.response ? item.response.id : item.id
}) })
} }
axios.put(editUrl, { axios.put(editUrl, {
subject: values.subject, subject: values.subject,
select_board_id: values.select_board_id, select_board_id: values.select_board_id,
content: values.content, content: values.content,
sticky: values.sticky, sticky: values.sticky,
attachment_ids, attachment_ids,
}) })
.then((response) => { .then((response) => {
if (response.data.status == 0) { if (response.data.status == 0) {
const { id } = response.data; const { id } = response.data;
console.log('--- success') console.log('--- success')
this.props.toDetailPage(cid, values.select_board_id, editTopic.id) this.props.toDetailPage(cid, values.select_board_id, editTopic.id)
} }
}) })
.catch(function (error) { .catch(function (error) {
console.log(error); console.log(error);
}); });
} else { } else {
const url = `/boards/${boardId}/messages.json` const url = `/boards/${boardId}/messages.json`
let attachment_ids = undefined let attachment_ids = undefined
if (this.state.fileList) { if (this.state.fileList) {
attachment_ids = this.state.fileList.map(item => { attachment_ids = this.state.fileList.map(item => {
return item.response.id return item.response.id
}) })
} }
axios.post(url, { axios.post(url, {
...values, ...values,
course_id: cid, course_id: cid,
attachment_ids, attachment_ids,
}) })
.then((response) => { .then((response) => {
if (response.data.data && response.data.status == 0) { if (response.data.data && response.data.status == 0) {
const { id } = response.data.data; const { id } = response.data.data;
if (id) { if (id) {
console.log('--- success') console.log('--- success')
this.props.toDetailPage(cid, values.select_board_id, id) this.props.toDetailPage(cid, values.select_board_id, id)
} }
} }
}) })
.catch(function (error) { .catch(function (error) {
console.log(error); console.log(error);
}); });
} }
} else { } else {
$("html").animate({ scrollTop: $('html').scrollTop() - 100 }) $("html").animate({ scrollTop: $('html').scrollTop() - 100 })
} }
}); });
} }
// 附件相关 START // 附件相关 START
handleChange = (info) => { handleChange = (info) => {
let fileList = info.fileList; let fileList = info.fileList;
this.setState({ fileList: appendFileSizeToUploadFileAll(fileList) this.setState({ fileList: appendFileSizeToUploadFileAll(fileList)
}); });
} }
onAttachmentRemove = (file) => { onAttachmentRemove = (file) => {
confirm({ confirm({
// title: '确定要删除这个附件吗?', // title: '确定要删除这个附件吗?',
title: '是否确认删除?', title: '是否确认删除?',
okText: '确定', okText: '确定',
cancelText: '取消', cancelText: '取消',
// content: 'Some descriptions', // content: 'Some descriptions',
onOk: () => { onOk: () => {
this.deleteAttachment(file) this.deleteAttachment(file)
}, },
onCancel() { onCancel() {
console.log('Cancel'); console.log('Cancel');
}, },
}); });
return false; return false;
} }
deleteAttachment = (file) => { deleteAttachment = (file) => {
// 初次上传不能直接取uid // 初次上传不能直接取uid
const url = `/attachments/${file.response ? file.response.id : file.uid}.json` const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
axios.delete(url, { axios.delete(url, {
}) })
.then((response) => { .then((response) => {
if (response.data) { if (response.data) {
const { status } = response.data; const { status } = response.data;
if (status == 0) { if (status == 0) {
console.log('--- success') console.log('--- success')
this.setState((state) => { this.setState((state) => {
const index = state.fileList.indexOf(file); const index = state.fileList.indexOf(file);
const newFileList = state.fileList.slice(); const newFileList = state.fileList.slice();
newFileList.splice(index, 1); newFileList.splice(index, 1);
return { return {
fileList: newFileList, fileList: newFileList,
}; };
}); });
} }
} }
}) })
.catch(function (error) { .catch(function (error) {
console.log(error); console.log(error);
}); });
} }
// 附件相关 ------------ END // 附件相关 ------------ END
changeTitle=(e)=>{ changeTitle=(e)=>{
console.log(e.target.value.length); console.log(e.target.value.length);
this.setState({ this.setState({
title_num: 60 - parseInt(e.target.value.length) title_num: 60 - parseInt(e.target.value.length)
}) })
} }
render() { render() {
let { addGroup, fileList, course_id, title_num } = this.state; let { addGroup, fileList, course_id, title_num } = this.state;
const { getFieldDecorator } = this.props.form; const { getFieldDecorator } = this.props.form;
const { current_user } = this.props const { current_user } = this.props
const formItemLayout = { const formItemLayout = {
labelCol: { labelCol: {
xs: { span: 24 }, xs: { span: 24 },
// sm: { span: 8 }, // sm: { span: 8 },
sm: { span: 24 }, sm: { span: 24 },
}, },
wrapperCol: { wrapperCol: {
xs: { span: 24 }, xs: { span: 24 },
// sm: { span: 16 }, // sm: { span: 16 },
sm: { span: 24 }, sm: { span: 24 },
}, },
}; };
const uploadProps = { const uploadProps = {
width: 600, width: 600,
fileList, fileList,
multiple: true, multiple: true,
// https://github.com/ant-design/ant-design/issues/15505 // https://github.com/ant-design/ant-design/issues/15505
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。 // showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
// showUploadList: false, // showUploadList: false,
action: `${getUploadActionUrl()}`, action: `${getUploadActionUrl()}`,
onChange: this.handleChange, onChange: this.handleChange,
onRemove: this.onAttachmentRemove, onRemove: this.onAttachmentRemove,
beforeUpload: (file) => { beforeUpload: (file) => {
console.log('beforeUpload', file.name); console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 < 150; const isLt150M = file.size / 1024 / 1024 < 150;
if (!isLt150M) { if (!isLt150M) {
message.error('文件大小必须小于150MB!'); message.error('文件大小必须小于150MB!');
} }
return isLt150M; return isLt150M;
}, },
}; };
const isAdmin = this.props.isAdmin() const isAdmin = this.props.isAdmin()
const courseId=this.props.match.params.coursesId; const courseId=this.props.match.params.coursesId;
const boardId = this.props.match.params.boardId const boardId = this.props.match.params.boardId
return( return(
<div className="newMain "> <div className="newMain ">
<AddDirModal {...this.props} <AddDirModal {...this.props}
title="添加目录" title="添加目录"
label="目录名称" label="目录名称"
ref="addDirModal" ref="addDirModal"
addSuccess={this.addSuccess} addSuccess={this.addSuccess}
></AddDirModal> ></AddDirModal>
<style>{` <style>{`
.courseForm .ant-form { .courseForm .ant-form {
} }
.courseForm .formBlock { .courseForm .formBlock {
padding: 20px 30px 30px 30px; padding: 20px 30px 30px 30px;
border-bottom: 1px solid #EDEDED; border-bottom: 1px solid #EDEDED;
margin-bottom: 0px; margin-bottom: 0px;
background: #fff; background: #fff;
} }
.courseForm .noBorder { .courseForm .noBorder {
border-bottom: none; border-bottom: none;
} }
`}</style> `}</style>
<div className="edu-class-container edu-position courseForm"> <div className="edu-class-container edu-position courseForm">
<CBreadcrumb items={[ <CBreadcrumb items={[
{ to: current_user.first_category_url, name: this.props.coursedata ? this.props.coursedata.name : ''}, { to: current_user&&current_user.first_category_url, name: this.props.coursedata ? this.props.coursedata.name : ''},
{ to: `/courses/${courseId}/boards/${boardId}`, name: this.state.board_name }, { to: `/courses/${courseId}/boards/${boardId}`, name: this.state.board_name },
{ name: this.isEdit ? '帖子编辑' : '帖子新建'} { name: this.isEdit ? '帖子编辑' : '帖子新建'}
]}></CBreadcrumb> ]}></CBreadcrumb>
<p className="clearfix mt20 mb20"> <p className="clearfix mt20 mb20">
<span className="fl font-24 color-grey-3">{this.isEdit ? "编辑" : "新建"}帖子</span> <span className="fl font-24 color-grey-3">{this.isEdit ? "编辑" : "新建"}帖子</span>
<a href="javascript:void(0)" className="color-grey-6 fr font-16 mr2" <a href="javascript:void(0)" className="color-grey-6 fr font-16 mr2"
onClick={() => this.props.history.goBack()}> onClick={() => this.props.history.goBack()}>
返回 返回
</a> </a>
</p> </p>
{/* notRequired */} {/* notRequired */}
<Form {...formItemLayout} onSubmit={this.handleSubmit}> <Form {...formItemLayout} onSubmit={this.handleSubmit}>
<div className="formBlock" style={{paddingBottom: '0px', position: 'relative'}}> <div className="formBlock" style={{paddingBottom: '0px', position: 'relative'}}>
{ isAdmin && { isAdmin &&
<React.Fragment> <React.Fragment>
{getFieldDecorator('sticky', { {getFieldDecorator('sticky', {
valuePropName: 'checked', valuePropName: 'checked',
})( })(
isAdmin && <Checkbox style={{ right: '22px', isAdmin && <Checkbox style={{ right: '22px',
top: '28px', top: '28px',
position: 'absolute' position: 'absolute'
}}>置顶</Checkbox> }}>置顶</Checkbox>
)} )}
{/* checkbox 有个边距样式 .ant-checkbox-wrapper + span, */} {/* checkbox 有个边距样式 .ant-checkbox-wrapper + span, */}
<span style={{ "padding-left": 0, "padding-right": 0 }}></span> <span style={{ "padding-left": 0, "padding-right": 0 }}></span>
</React.Fragment> </React.Fragment>
} }
<Form.Item <Form.Item
label="标题" label="标题"
className="topicTitle " className="topicTitle "
> >
{getFieldDecorator('subject', { {getFieldDecorator('subject', {
rules: [{ rules: [{
required: true, message: '请输入标题', required: true, message: '请输入标题',
}, { }, {
max: 60, message: '最大限制为60个字符', max: 60, message: '最大限制为60个字符',
}], }],
})( })(
<Input placeholder="请输入帖子标题最大限制60个字符" className="searchViewAfter" maxLength="60" <Input placeholder="请输入帖子标题最大限制60个字符" className="searchViewAfter" maxLength="60"
onInput={this.changeTitle} addonAfter={String(title_num)} /> onInput={this.changeTitle} addonAfter={String(title_num)} />
)} )}
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="" label=""
style={{ display: 'inline-block' }} style={{ display: 'inline-block' }}
> >
{getFieldDecorator('select_board_id', { {getFieldDecorator('select_board_id', {
// initialValue: '3779', // initialValue: '3779',
})( })(
<Select style={{ width: 230 }} <Select style={{ width: 230 }}
dropdownRender={menu => ( dropdownRender={menu => (
<div> <div>
{menu} {menu}
<Divider style={{ margin: '4px 0' }} /> <Divider style={{ margin: '4px 0' }} />
<div style={{ padding: '8px', cursor: 'pointer' }} onMouseDown={() => this.refs['addDirModal'].open()}> <div style={{ padding: '8px', cursor: 'pointer' }} onMouseDown={() => this.refs['addDirModal'].open()}>
<Icon type="plus" /> 添加目录 <Icon type="plus" /> 添加目录
</div> </div>
</div> </div>
)} )}
> >
{this.state.boards.map(item => { {this.state.boards.map(item => {
return ( return (
<Option value={item.id}>{item.name}</Option> <Option value={item.id}>{item.name}</Option>
) )
})} })}
</Select> </Select>
)} )}
</Form.Item> </Form.Item>
{/* { isAdmin && <Form.Item {/* { isAdmin && <Form.Item
label="" label=""
style={{ display: 'inline-block', marginLeft: "14px" }} style={{ display: 'inline-block', marginLeft: "14px" }}
> >
{getFieldDecorator('sticky', { {getFieldDecorator('sticky', {
})( })(
<Checkbox>置顶</Checkbox> <Checkbox>置顶</Checkbox>
)} )}
</Form.Item> } */} </Form.Item> } */}
</div> </div>
<style>{` <style>{`
.courseMessageMD { .courseMessageMD {
width: 1140px; width: 1140px;
} }
.uploadBtn.ant-btn { .uploadBtn.ant-btn {
border: none; border: none;
color: #4CACFF; color: #4CACFF;
box-shadow: none; box-shadow: none;
background: transparent; background: transparent;
padding: 0 6px; padding: 0 6px;
} }
.upload_1 .ant-upload-list { .upload_1 .ant-upload-list {
width: 350px; width: 350px;
} }
`}</style> `}</style>
<div className="formBlock noBorder"> <div className="formBlock noBorder">
<Form.Item <Form.Item
label="内容" label="内容"
className="mdInForm" className="mdInForm"
> >
{getFieldDecorator('content', { {getFieldDecorator('content', {
rules: [{ rules: [{
required: true, message: '请输入帖子内容', required: true, message: '请输入帖子内容',
}, { }, {
max: 10000, message: '最大限制为10000个字符', max: 10000, message: '最大限制为10000个字符',
}], }],
})( })(
<TPMMDEditor ref={this.mdRef} placeholder={'请在此输入帖子详情最大限制为10000个字符'} watch={false} <TPMMDEditor ref={this.mdRef} placeholder={'请在此输入帖子详情最大限制为10000个字符'}
mdID={'courseMessageMD'} initValue={this.editTopic ? this.editTopic.content : ''} className="courseMessageMD"></TPMMDEditor> mdID={'courseMessageMD'} initValue={this.editTopic ? this.editTopic.content : ''} className="courseMessageMD"></TPMMDEditor>
)} )}
</Form.Item> </Form.Item>
<Upload {...uploadProps} className="upload_1"> <Upload {...uploadProps} className="upload_1">
<Button className="uploadBtn"> <Button className="uploadBtn">
<Icon type="upload" /> 上传附件 <Icon type="upload" /> 上传附件
</Button> </Button>
(单个文件150M以内) (单个文件150M以内)
</Upload> </Upload>
</div> </div>
<Form.Item> <Form.Item>
<div className="clearfix mt30 mb30"> <div className="clearfix mt30 mb30">
<Button type="primary" htmlType="submit" className="defalutSubmitbtn fl mr20">提交</Button> <Button type="primary" htmlType="submit" className="defalutSubmitbtn fl mr20">提交</Button>
<a className="defalutCancelbtn fl" <a className="defalutCancelbtn fl"
onClick={() => this.isEdit ? onClick={() => this.isEdit ?
this.props.toDetailPage(Object.assign({}, this.props.match.params, {'coursesId': course_id})) : this.props.toDetailPage(Object.assign({}, this.props.match.params, {'coursesId': course_id})) :
this.props.toListPage(Object.assign({}, this.props.match.params, {'coursesId': course_id})) }>取消</ a> this.props.toListPage(Object.assign({}, this.props.match.params, {'coursesId': course_id})) }>取消</ a>
</div> </div>
</Form.Item> </Form.Item>
</Form> </Form>
</div> </div>
</div> </div>
) )
} }
} }
const WrappedBoardsNew = Form.create({ name: 'boardsNew' })(BoardsNew); const WrappedBoardsNew = Form.create({ name: 'boardsNew' })(BoardsNew);
export default RouteHOC()(WrappedBoardsNew); export default RouteHOC()(WrappedBoardsNew);

File diff suppressed because it is too large Load Diff

@ -440,7 +440,7 @@ class Boards extends Component{
</div> </div>
</Spin> </Spin>
{ {
( !messages || messages.length == 0 ) && <NoneData></NoneData> ( !this.state.isSpin && (!messages || messages.length == 0) ) && <NoneData></NoneData>
} }
{/* { haveMore && <p className="edu-txt-center pt30 pb10 clearfix"> {/* { haveMore && <p className="edu-txt-center pt30 pb10 clearfix">

@ -223,7 +223,7 @@ class CommonWorkDetailIndex extends Component{
} }
`}</style> `}</style>
{current_user && <CBreadcrumb items={[ {current_user && <CBreadcrumb items={[
{ to: current_user.first_category_url , name: course_name}, { to: current_user&&current_user.first_category_url , name: course_name},
{ to: `/courses/${courseId}/${moduleEngName}/${category_id}`, name: category_name }, { to: `/courses/${courseId}/${moduleEngName}/${category_id}`, name: category_name },
window.location.pathname.indexOf('appraise') == -1 ? { } : { to: `/courses/${courseId}/${moduleEngName}/${workId}/list`, name: '作业详情' }, window.location.pathname.indexOf('appraise') == -1 ? { } : { to: `/courses/${courseId}/${moduleEngName}/${workId}/list`, name: '作业详情' },
// 1. 与上一条联动当匿评他人作品时TA人作品的作者真实姓名切换为“匿名” // 1. 与上一条联动当匿评他人作品时TA人作品的作者真实姓名切换为“匿名”

@ -722,7 +722,7 @@ class CommonWorkSetting extends Component{
late_time: late_time ? new Date(late_time) : late_time, // 补交截止时间 late_time: late_time ? new Date(late_time) : late_time, // 补交截止时间
anonymous_comment: anonymous_comment, // true: 启用匿评 false:未启用匿评 anonymous_comment: anonymous_comment, // true: 启用匿评 false:未启用匿评
evaluation_start: evaluation_start ? new Date(evaluation_start) : evaluation_start, //匿评开始时间 evaluation_start: evaluation_start ? new Date(evaluation_start) : evaluation_start, //匿评开始时间
evaluation_end: evaluation_end, evaluation_end: evaluation_end ? new Date(evaluation_end) : evaluation_end,
evaluation_num: evaluation_num, // 匿评数 evaluation_num: evaluation_num, // 匿评数
absence_penalty: absence_penalty, // 匿评扣分 absence_penalty: absence_penalty, // 匿评扣分
anonymous_appeal: anonymous_appeal, // true: 启用匿评申诉, false:未启用 anonymous_appeal: anonymous_appeal, // true: 启用匿评申诉, false:未启用

@ -446,7 +446,7 @@ class NewWork extends Component{
}], }],
})( })(
<TPMMDEditor ref={this.contentMdRef} placeholder="请在此输入作业内容和要求,最大限制5000个字符" mdID={'courseContentMD'} refreshTimeout={1500} <TPMMDEditor ref={this.contentMdRef} placeholder="请在此输入作业内容和要求,最大限制5000个字符" mdID={'courseContentMD'} refreshTimeout={1500}
watch={false} className="courseMessageMD" initValue={this.state.description}></TPMMDEditor> className="courseMessageMD" initValue={this.state.description}></TPMMDEditor>
)} )}
</Form.Item> } </Form.Item> }
<Upload {...uploadProps} className="upload_1 newWorkUpload"> <Upload {...uploadProps} className="upload_1 newWorkUpload">
@ -508,7 +508,7 @@ class NewWork extends Component{
required: false required: false
}], }],
})( })(
<TPMMDEditor ref={this.answerMdRef} placeholder="请在此输入作业的参考答案,最大限制5000个字符" mdID={'workAnswerMD'} watch={false} <TPMMDEditor ref={this.answerMdRef} placeholder="请在此输入作业的参考答案,最大限制5000个字符" mdID={'workAnswerMD'}
className="courseMessageMD" refreshTimeout={1500} initValue={this.state.reference_answer || ''}></TPMMDEditor> className="courseMessageMD" refreshTimeout={1500} initValue={this.state.reference_answer || ''}></TPMMDEditor>
)} )}
<Upload {...answerUploadProps} className="upload_1"> <Upload {...answerUploadProps} className="upload_1">

@ -90,7 +90,7 @@ class WorkDetailPageHeader extends Component{
} }
`}</style> `}</style>
<CBreadcrumb items={[ <CBreadcrumb items={[
{ to: current_user.first_category_url, name: course_name}, { to: current_user&&current_user.first_category_url, name: course_name},
{ to: `/courses/${courseId}/${moduleEngName}/${category_id}`, name: category_name }, { to: `/courses/${courseId}/${moduleEngName}/${category_id}`, name: category_name },
window.location.pathname.indexOf('appraise') == -1 ? { } : { to: `/courses/${courseId}/${moduleEngName}/${workId}/list`, name: '作业详情' }, window.location.pathname.indexOf('appraise') == -1 ? { } : { to: `/courses/${courseId}/${moduleEngName}/${workId}/list`, name: '作业详情' },
// 1. 与上一条联动当匿评他人作品时TA人作品的作者真实姓名切换为“匿名” // 1. 与上一条联动当匿评他人作品时TA人作品的作者真实姓名切换为“匿名”

@ -75,6 +75,7 @@ class Addcourses extends Component{
if(e.target.checked===true){ if(e.target.checked===true){
this.setState({ this.setState({
assistant_professor:1, assistant_professor:1,
professor:null,
Checkboxteachingchecked:e.target.checked, Checkboxteachingchecked:e.target.checked,
Checkboxteachertype:true Checkboxteachertype:true
}) })
@ -95,6 +96,7 @@ class Addcourses extends Component{
if(e.target.checked===true){ if(e.target.checked===true){
this.setState({ this.setState({
professor:1, professor:1,
assistant_professor:null,
Checkboxteacherchecked:e.target.checked, Checkboxteacherchecked:e.target.checked,
Checkboxteachingtype:true Checkboxteachingtype:true
}) })

@ -57,7 +57,7 @@ class SchoolSelect extends Component{
const { value, onChange } = this.props; const { value, onChange } = this.props;
return ( return (
<AutoComplete allowClear placeholder="请输入单位名称" value={value} <AutoComplete allowClear placeholder="请输入单位名称" value={value}
style={{ width: '200px'}} style={{ width: '221px'}}
onSearch={this.onOrgNameSearch} onSearch={this.onOrgNameSearch}
onSelect={onChange} onSelect={onChange}
onChange={onChange} onChange={onChange}

@ -467,7 +467,7 @@ class Sendresource extends Component{
{/*)*/} {/*)*/}
{/*})}*/} {/*})}*/}
{newfileListtype===true?<p className={"color-red"}>请先上传资源</p>:""} {newfileListtype===true&&this.state.fileListtype===false?<p className={"color-red"}>请先上传资源</p>:""}
<p className={"winth540"}> <p className={"winth540"}>
<style>{` <style>{`

File diff suppressed because it is too large Load Diff

@ -171,7 +171,8 @@ class Testpapersettinghomepage extends Component{
} }
console.log("170"); console.log("170");
console.log(params); console.log(params);
axios.get(url+`?${queryString.stringify(params)}`+ '&export=true').then((response) => { const urll=url+`?${queryString.stringify(params)}`;
axios.get(urll+ '&export=true').then((response) => {
if(response===undefined){ if(response===undefined){
return return
} }
@ -195,13 +196,13 @@ class Testpapersettinghomepage extends Component{
}else { }else {
this.setState({ donwloading: true }) this.setState({ donwloading: true })
downloadFile({ downloadFile({
url: url+`?${queryString.stringify(params)}`, url: urll,
successCallback: (url) => { successCallback: (url) => {
this.setState({ donwloading: false }); this.setState({ donwloading: false })
console.log('successCallback') console.log('successCallback')
}, },
failCallback: (responseHtml, url) => { failCallback: (responseHtml, url) => {
this.setState({ donwloading: false }); this.setState({ donwloading: false })
console.log('failCallback') console.log('failCallback')
} }
}) })

@ -187,7 +187,7 @@ class SingleEditor extends Component{
<span className="color-grey-9 font-12 fl">客观题由系统自动评分请设置标准答案</span> <span className="color-grey-9 font-12 fl">客观题由系统自动评分请设置标准答案</span>
</p> </p>
<TPMMDEditor mdID={`question_${question_id}`} placeholder="请您输入题目" height={155} watch={false} <TPMMDEditor mdID={`question_${question_id}`} placeholder="请您输入题目" height={155}
initValue={question_title} onChange={(val) => this.setState({ question_title: val})} initValue={question_title} onChange={(val) => this.setState({ question_title: val})}
ref="titleEditor" ref="titleEditor"
></TPMMDEditor> ></TPMMDEditor>

@ -152,14 +152,14 @@ class MainEditor extends Component{
<span className="color-grey-9 font-12 fl">主观题未作答的情况下自动评为零分</span> <span className="color-grey-9 font-12 fl">主观题未作答的情况下自动评为零分</span>
</p> </p>
<TPMMDEditor mdID={`question_${question_id}`} placeholder="请您输入题目" height={155} watch={false} <TPMMDEditor mdID={`question_${question_id}`} placeholder="请您输入题目" height={155}
initValue={question_title} onChange={(val) => this.setState({ question_title: val})} initValue={question_title} onChange={(val) => this.setState({ question_title: val})}
noStorage={true} ref="titleEditor" noStorage={true} ref="titleEditor"
></TPMMDEditor> ></TPMMDEditor>
<div> <div>
<div className="mb10 font-16">参考答案</div> <div className="mb10 font-16">参考答案</div>
<TPMMDEditor mdID={`question_answer_${question_id}`} placeholder="请输入参考答案(可选)" height={155} watch={false} <TPMMDEditor mdID={`question_answer_${question_id}`} placeholder="请输入参考答案(可选)" height={155}
initValue={standard_answers[0] || ''} onChange={(val) => this.setState({ standard_answers: [val]})} initValue={standard_answers[0] || ''} onChange={(val) => this.setState({ standard_answers: [val]})}
noStorage={true} noStorage={true}
></TPMMDEditor> ></TPMMDEditor>

@ -57,7 +57,7 @@ class NullChildEditor extends Component{
className={'nullChildEditor'} className={'nullChildEditor'}
placeholder={`请输入参考答案${itemIndex == 0 ?'':'(可选)'}`} placeholder={`请输入参考答案${itemIndex == 0 ?'':'(可选)'}`}
toMDMode={toMDMode} noStorage={true} toMDMode={toMDMode} noStorage={true}
mdID={`answer_${index}${itemIndex}`} height={155} watch={false} mdID={`answer_${index}${itemIndex}`} height={155}
initValue={item} onChange={(val) => onAnswerChange(index, itemIndex, val)} initValue={item} onChange={(val) => onAnswerChange(index, itemIndex, val)}
></DMDEditor> ></DMDEditor>
</div> </div>

@ -297,7 +297,7 @@ class NullEditor extends Component{
<span className="color-grey-9 font-12 fl">客观题由系统自动评分允许手动调分请设置标准答案 支持最多5个空每空得分按照本题的总分平均计算</span> <span className="color-grey-9 font-12 fl">客观题由系统自动评分允许手动调分请设置标准答案 支持最多5个空每空得分按照本题的总分平均计算</span>
</p> </p>
<NullMDEditor {...this.props} mdID={`question_${question_id}`} placeholder="请您输入题目" height={155} watch={false} <NullMDEditor {...this.props} mdID={`question_${question_id}`} placeholder="请您输入题目" height={155}
initValue={question_title} onChange={(val) => this.setState({ question_title: val})} initValue={question_title} onChange={(val) => this.setState({ question_title: val})}
onPlaceholderChange={this.onPlaceholderChange} showNullButton={exerciseIsPublish ? false : true} onPlaceholderChange={this.onPlaceholderChange} showNullButton={exerciseIsPublish ? false : true}
ref="titleEditor" ref="titleEditor"

@ -250,7 +250,7 @@ class ShixunEditor extends Component{
style={{ marginBottom: '10px'}} style={{ marginBottom: '10px'}}
></Input> ></Input>
{/* <div style={{color: blackColor}} className="font-16 mb5">{shixun_name}</div> */} {/* <div style={{color: blackColor}} className="font-16 mb5">{shixun_name}</div> */}
<TPMMDEditor mdID={`question_${question_id}`} placeholder="请输入实训题完成要求" height={155} watch={false} <TPMMDEditor mdID={`question_${question_id}`} placeholder="请输入实训题完成要求" height={155}
initValue={question_title} onChange={(val) => this.setState({ question_title: val})} initValue={question_title} onChange={(val) => this.setState({ question_title: val})}
noStorage={true} noStorage={true}
></TPMMDEditor> ></TPMMDEditor>

@ -242,7 +242,7 @@ class SingleEditor extends Component{
<span className="color-grey-9 font-12 fl">客观题由系统自动评分请设置标准答案</span> <span className="color-grey-9 font-12 fl">客观题由系统自动评分请设置标准答案</span>
</p> </p>
<TPMMDEditor mdID={qNumber} placeholder="请您输入题目" height={155} watch={false} className="mb20" <TPMMDEditor mdID={qNumber} placeholder="请您输入题目" height={155} className="mb20"
initValue={question_title} onChange={(val) => this.setState({ question_title: val})} initValue={question_title} onChange={(val) => this.setState({ question_title: val})}
ref="titleEditor" ref="titleEditor"
@ -265,7 +265,7 @@ class SingleEditor extends Component{
<DMDEditor <DMDEditor
ref={`optionEditor${index}`} ref={`optionEditor${index}`}
toMDMode={this.toMDMode} toShowMode={this.toShowMode} toMDMode={this.toMDMode} toShowMode={this.toShowMode}
height={166} className={'optionMdEditor'} watch={false} noStorage={true} height={166} className={'optionMdEditor'} noStorage={true}
mdID={qNumber + index} placeholder="" onChange={(value) => this.onOptionContentChange(value, index)} mdID={qNumber + index} placeholder="" onChange={(value) => this.onOptionContentChange(value, index)}
initValue={item} initValue={item}
></DMDEditor> ></DMDEditor>

@ -767,7 +767,7 @@ class GraduationTasks extends Component{
<div className="alltask edu-back-white" <div className="alltask edu-back-white"
style={ style={
{ {
display: all_count===undefined?'block' :all_count===0? 'block' : 'none' display: all_count===undefined?'none' :all_count===0? 'block' : 'none'
} }
} }
> >

@ -105,7 +105,7 @@ class GraduateTopicDetail extends Component{
<div className="newMain"> <div className="newMain">
<div className="educontent mt10 mb50"> <div className="educontent mt10 mb50">
<p className="clearfix mb15 lineh-20"> <p className="clearfix mb15 lineh-20">
<WordsBtn style="grey" className="fl" to={current_user.first_category_url}>{tableData && tableData.course_name}</WordsBtn> <WordsBtn style="grey" className="fl" to={current_user&&current_user.first_category_url}>{tableData && tableData.course_name}</WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span> <span className="color-grey-9 fl ml3 mr3">&gt;</span>
<WordsBtn style="grey" className="fl" to={`/courses/${tableData.course_id}/graduation_topics/${tableData.graduation_id}`}>{tableData.graduation_name}</WordsBtn> <WordsBtn style="grey" className="fl" to={`/courses/${tableData.course_id}/graduation_topics/${tableData.graduation_id}`}>{tableData.graduation_name}</WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span> <span className="color-grey-9 fl ml3 mr3">&gt;</span>

@ -323,7 +323,7 @@ class GraduateTopicNew extends Component{
`}</style> `}</style>
<div className="edu-class-container edu-position courseForm"> <div className="edu-class-container edu-position courseForm">
<p className="clearfix mb20 mt10"> <p className="clearfix mb20 mt10">
<WordsBtn style="grey" className="fl" to={current_user.first_category_url}>{course_name}</WordsBtn> <WordsBtn style="grey" className="fl" to={current_user&&current_user.first_category_url}>{course_name}</WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span> <span className="color-grey-9 fl ml3 mr3">&gt;</span>
<WordsBtn style="grey" className="fl" to={`/courses/${coursesId}/graduation_topics/${left_banner_id}`}>{left_banner_name}</WordsBtn> <WordsBtn style="grey" className="fl" to={`/courses/${coursesId}/graduation_topics/${left_banner_id}`}>{left_banner_name}</WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span> <span className="color-grey-9 fl ml3 mr3">&gt;</span>

@ -28,7 +28,7 @@ class Boards extends Component{
checkAllValue: false, checkAllValue: false,
checkBoxValues: [], checkBoxValues: [],
data:[], data:[],
topicList: [], topicList: undefined,
course_public:1, course_public:1,
page:1, page:1,
totalCount:undefined, totalCount:undefined,
@ -412,7 +412,7 @@ onBoardsNew=()=>{
} }
onPressEnter={this.onPressEnter} onPressEnter={this.onPressEnter}
searchPlaceholder={"请输入名称进行搜索"} searchPlaceholder={"请输入名称进行搜索"}
showSearchInput={topicList.length >= pageSize} showSearchInput={topicList&&topicList.length >= pageSize}
></Titlesearchsection> ></Titlesearchsection>
<Spin size="large" spinning={this.state.isSpin}> <Spin size="large" spinning={this.state.isSpin}>
{ {
@ -475,7 +475,7 @@ onBoardsNew=()=>{
> >
</Modals> </Modals>
</div> </div>
):(<NoneData></NoneData>) ):(topicList && topicList.length===0?<NoneData></NoneData>:<div className="clearfix stu_table mt20"></div>)
} }
</Spin> </Spin>
{ {

@ -195,7 +195,7 @@ class AddStudentModal extends Component{
<div className="df"> <div className="df">
<span className="mr10">姓名:</span> <span className="mr10">姓名:</span>
<Input allowClear placeholder="请输入真实姓名" value={name} onChange={(e) => {this.setState({name: e.target.value})}} <Input allowClear placeholder="请输入真实姓名" value={name} onChange={(e) => {this.setState({name: e.target.value})}}
style={{ width: '242px'}} style={{ width: '221px'}}
></Input> ></Input>
<span className="label" style={{ minWidth: '36px' }}>单位:</span> <span className="label" style={{ minWidth: '36px' }}>单位:</span>
{/* <Input allowClear placeholder="" value={school_name} onChange={(e) => {this.setState({school_name: e.target.value})}} {/* <Input allowClear placeholder="" value={school_name} onChange={(e) => {this.setState({school_name: e.target.value})}}
@ -210,14 +210,7 @@ class AddStudentModal extends Component{
>搜索</a> >搜索</a>
</div> </div>
{/* <Divider /> */} {/* <Divider /> */}
{course_groups && course_groups.length && <div className="df" style={{ marginTop: '24px' }} >
<span className="mr10">分班:</span>
<Select style={{ width:500 }} onChange={this.handleCourseGroupChange} value={courseGroup}>
{ course_groups.map((item) => {
return <Option value={item.id}>{item.name}</Option>
})}
</Select>
</div>}
<p className="clearfix mb2" style={{ margin: '0px 15px 6px' }}> <p className="clearfix mb2" style={{ margin: '0px 15px 6px' }}>
<Checkbox className="fl" style={{ visibility: 'hidden' }} ></Checkbox> <Checkbox className="fl" style={{ visibility: 'hidden' }} ></Checkbox>
@ -247,7 +240,12 @@ class AddStudentModal extends Component{
<Checkbox className="fl" value={candidate.id} disabled={candidate.added}></Checkbox> <Checkbox className="fl" value={candidate.id} disabled={candidate.added}></Checkbox>
<span className="fl with25"> <span className="fl with25">
<ConditionToolTip title={candidate.name} condition={candidate.name && candidate.name.length > 12 }> <ConditionToolTip title={candidate.name} condition={candidate.name && candidate.name.length > 12 }>
<label className="task-hide fl" style={{"maxWidth":"208px;"}}>{candidate.name || ' '}</label> <label className="task-hide fl" style={{"maxWidth":"208px;"}}>
{ candidate.name ?
<a href={`/users/${candidate.login}`} target="_blank">
{ candidate.name }
</a> : <span> </span> }
</label>
</ConditionToolTip> </ConditionToolTip>
</span> </span>
<span className="fl with25"> <span className="fl with25">
@ -271,6 +269,14 @@ class AddStudentModal extends Component{
</InfiniteScroll> </InfiniteScroll>
</div> </div>
{course_groups && course_groups.length && <div className="df" style={{ marginTop: '12px' }} >
<span className="mr10" style={{ width: '148px' }}>所选学生分班至(选填):</span>
<Select style={{ width: 236 }} onChange={this.handleCourseGroupChange} value={courseGroup}>
{ course_groups.map((item) => {
return <Option value={item.id}>{item.name}</Option>
})}
</Select>
</div>}
</div> : <NoneData></NoneData> } </div> : <NoneData></NoneData> }
</Spin> </Spin>
</ModalWrapper> </ModalWrapper>

@ -677,9 +677,9 @@ class studentsList extends Component{
</div> </div>
<Spin size="large" spinning={this.state.isSpin}> <Spin size="large" spinning={this.state.isSpin}>
<div className="clearfix stu_table"> <div className="clearfix stu_table">
<Checkbox.Group style={{ width: '100%' }} onChange={this.onCheckBoxChange} value={checkBoxValues}> {!this.state.isSpin && <Checkbox.Group style={{ width: '100%' }} onChange={this.onCheckBoxChange} value={checkBoxValues}>
<Table columns={buildColumns(this)} dataSource={students} pagination={false}></Table> <Table columns={buildColumns(this)} dataSource={students} pagination={false}></Table>
</Checkbox.Group> </Checkbox.Group> }
</div> </div>
</Spin> </Spin>
</div> </div>

@ -603,7 +603,9 @@ class studentsList extends Component{
combineArray = this.state.application_list combineArray = this.state.application_list
} }
const isAdminOrTeacher = this.props.isAdminOrTeacher() const isAdminOrTeacher = this.props.isAdminOrTeacher()
const isSuperAdmin = this.props.isSuperAdmin()
const hasGraduationModule = this.hasGraduationModule() const hasGraduationModule = this.hasGraduationModule()
const coursesId = this.props.match.params.coursesId
return( return(
<React.Fragment> <React.Fragment>
@ -624,7 +626,10 @@ class studentsList extends Component{
></AddAdminModal> ></AddAdminModal>
<Titlesearchsection <Titlesearchsection
title={"教师列表"} title={<React.Fragment>
<span>教师列表</span>
{!isSuperAdmin && coursesId == '1309' && <span style={{color: '#848484', fontSize: '14px', marginLeft: '10px'}}>(示例课堂部分成员不可见)</span>}
</React.Fragment>}
searchValue={ searchValue } searchValue={ searchValue }
onInputSearchChange={this.onInputSearchChange} onInputSearchChange={this.onInputSearchChange}
showSearchInput={total_count >= 10} showSearchInput={total_count >= 10}
@ -641,6 +646,7 @@ class studentsList extends Component{
<Menu.Item key="1">已审批({total_count})</Menu.Item> <Menu.Item key="1">已审批({total_count})</Menu.Item>
<Menu.Item key="2">待审批({apply_size})</Menu.Item> <Menu.Item key="2">待审批({apply_size})</Menu.Item>
</Menu> </Menu>
{/* */}
</div> : </div> :
(!!total_count ? <ColorCountText count={total_count} name="个教师"></ColorCountText> : '') (!!total_count ? <ColorCountText count={total_count} name="个教师"></ColorCountText> : '')
} }

@ -143,6 +143,7 @@ class CoursesNew extends Component {
handleSubmit = (e) => { handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
let first_category_url=this.props.current_user.first_category_url;
let coursesId = this.props.match.params.coursesId; let coursesId = this.props.match.params.coursesId;
let {is_public,datatime} = this.state let {is_public,datatime} = this.state
// console.log(is_public) // console.log(is_public)
@ -201,7 +202,8 @@ class CoursesNew extends Component {
).then((response) => { ).then((response) => {
// debugger // debugger
if (response.data.status === 0) { if (response.data.status === 0) {
this.goback() // this.goback()
window.location.href=first_category_url;
} }
}).catch((error) => { }).catch((error) => {
console.log(error) console.log(error)
@ -244,7 +246,8 @@ class CoursesNew extends Component {
} }
).then((response) => { ).then((response) => {
if (response.status === 200) { if (response.status === 200) {
this.goback() // this.goback
window.location.href=response.data.first_category_url;
} }
}).catch((error) => { }).catch((error) => {
console.log(error) console.log(error)

@ -1,6 +1,6 @@
import React,{ Component } from "react"; import React,{ Component } from "react";
import CoursesListType from '../coursesPublic/CoursesListType'; import CoursesListType from '../coursesPublic/CoursesListType';
import { WordsBtn } from 'educoder'; import { WordsBtn, getTaskUrlById } from 'educoder';
import {Tooltip,message,Modal} from 'antd'; import {Tooltip,message,Modal} from 'antd';
import {Link} from 'react-router-dom'; import {Link} from 'react-router-dom';
import axios from 'axios'; import axios from 'axios';
@ -56,16 +56,17 @@ class ShixunhomeWorkItem extends Component{
startbtn:true, startbtn:true,
}) })
let url= list+".json"; let url= list+".json";
// const w=window.open('about:blank');
axios.get(url).then((response) => { axios.get(url).then((response) => {
if(response.status===200){ if(response.status===200){
if(response.data.status===-2){ if(response.data.status===-2){
this.setState({ this.setState({
startbtn:false, startbtn:false,
shixunsreplace:true, shixunsreplace:true,
hidestartshixunsreplacevalue:response.data.message+".json" hidestartshixunsreplacevalue:response.data.message+".json"
}) })
// w.close()
}else if(response.data.status===-1){ }else if(response.data.status===-1){
console.log(response) console.log(response)
}else if(response.data.status===-3){ }else if(response.data.status===-3){
@ -74,13 +75,15 @@ class ShixunhomeWorkItem extends Component{
startshixunCombattype:true, startshixunCombattype:true,
startbtn:false startbtn:false
}) })
// w.close()
}else{ }else{
this.setState({
if(response.data.status!=401&&response.data.status!=403){ startbtn:false
})
const w=window.open('about:blank'); // if(response.data.status!=401&&response.data.status!=403){
w.location.href= "/tasks/"+response.data.game_identifier // w.location.href= "/tasks/"+response.data.game_identifier
} // }
window.location.href = "/tasks/"+response.data.game_identifier
} }
} }
@ -88,6 +91,8 @@ class ShixunhomeWorkItem extends Component{
this.setState({ this.setState({
startbtn:false startbtn:false
}) })
// w.close()
}); });
} }
@ -388,9 +393,15 @@ class ShixunhomeWorkItem extends Component{
{this.props.isStudent===true?this.props.course_identity===5? {this.props.isStudent===true?this.props.course_identity===5?
<WordsBtn style="blue" className="colorblue font-16 mr20 fr mt10"> <WordsBtn style="blue" className="colorblue font-16 mr20 fr mt10">
{startbtn===false?<a className="btn colorblue" onClick={()=>this.taskoperationId(discussMessage.task_operation[1])}> {startbtn===false?
{discussMessage.task_operation[0]} (discussMessage.task_operation[0] == '继续挑战' || discussMessage.task_operation[0] == '查看实战' ?
</a>:<a className="btn colorblue" ></a>} <a className="btn colorblue" href={getTaskUrlById(discussMessage.task_operation[1])}>
{discussMessage.task_operation[0]}
</a>
:
<a className="btn colorblue" onClick={()=>this.taskoperationId(discussMessage.task_operation[1])}>
{discussMessage.task_operation[0]}
</a>):<a className="btn colorblue" ></a>}
</WordsBtn>:"":"" </WordsBtn>:"":""
} }

@ -246,7 +246,7 @@ class TraineetraininginformationModal extends Component {
{this.props.boolgalist === false? {this.props.boolgalist === false?
<div> <div>
{ {
this.props.game_list === undefined?"" : this.props.game_list.length<4? this.props.game_list === undefined?"" : this.props.game_list.length<5?
<div className="edu-table edu-back-white "> <div className="edu-table edu-back-white ">
<style> <style>
{ {
@ -296,7 +296,7 @@ class TraineetraininginformationModal extends Component {
<div> <div>
{ {
this.props.game_list === undefined?"" : this.props.game_list.length<4? this.props.game_list === undefined?"" : this.props.game_list.length<5?
<div className="edu-table edu-back-white "> <div className="edu-table edu-back-white ">
<style> <style>
{ {

@ -1205,7 +1205,7 @@ class ShixunHomework extends Component{
<div className="alltask " <div className="alltask "
style={ style={
{ {
display: datas===undefined?'block' :datas.task_count===0? 'block' : 'none' display: datas===undefined?'none' :datas.task_count===0? 'block' : 'none'
} }
} }
> >

@ -5,6 +5,9 @@
.discuss-tab { .discuss-tab {
height: 90px; height: 90px;
} }
.discuss-tab ._forum_tab a.navItem {
line-height: 2;
}
.discuss-tab ._forum_tab a.navItem:hover { .discuss-tab ._forum_tab a.navItem:hover {
color: #4CACFF !important; color: #4CACFF !important;
border-bottom: none !important; border-bottom: none !important;

@ -42,6 +42,7 @@ import './ForumsIndex.css'
import './RightSection.css' import './RightSection.css'
import { SnackbarHOC, getUrl } from 'educoder' import { SnackbarHOC, getUrl } from 'educoder'
import { CNotificationHOC } from '../courses/common/CNotificationHOC'
let _url_origin = getUrl() let _url_origin = getUrl()
@ -248,7 +249,7 @@ class ForumsIndex extends Component {
} }
} }
export default SnackbarHOC() ( TPMIndexHOC ( ForumsIndex ) ); export default CNotificationHOC() (SnackbarHOC() ( TPMIndexHOC ( ForumsIndex ) ));
/* /*
: :

@ -44,7 +44,7 @@ class MemoDetail extends Component {
this.state = { this.state = {
memoLoading: true, memoLoading: true,
hasMoreComments: false, hasMoreComments: false,
pageCount: 1, pageCount: 2,
goldRewardDialogOpen: false goldRewardDialogOpen: false
} }
@ -54,18 +54,18 @@ class MemoDetail extends Component {
const { match } = this.props const { match } = this.props
const memoUrl = `/api/v1/memos/${match.params.memoId}`; const memoUrl = `/memos/${match.params.memoId}.json`;
this.setState({ this.setState({
memoLoading: true memoLoading: true
}) })
axios.get(memoUrl,{ axios.get(memoUrl,{
withCredentials: true, // withCredentials: true,
}) })
.then((response) => { .then((response) => {
const memo = response.data.memo const memo = response.data.memo
if (response.data.status === -1) { if (response.data.status === -1) {
setTimeout(() => { setTimeout(() => {
this.props.showSnackbar('帖子不存在!') this.props.showNotification('帖子不存在!')
}, 300) }, 300)
this.props.history.push(`/forums`) this.props.history.push(`/forums`)
return; return;
@ -86,14 +86,16 @@ class MemoDetail extends Component {
} }
this.setState({ this.setState({
hasMoreComments, hasMoreComments,
pageCount: 1, pageCount: 2,
comments: memo_replies comments: memo_replies
}) })
delete response.data.memo_replies; delete response.data.memo_replies;
// reset
response.data.memo.praise_count = response.data.memo.memo_praise_count
this.props.initForumState(response.data) this.props.initForumState(response.data)
const user = response.data.current_user; // const user = response.data.current_user;
user.tidding_count = response.data.tidding_count; // user.tidding_count = response.data.tidding_count;
this.props.initCommonState(user) // this.props.initCommonState(user)
} }
this.setState({ this.setState({
memoLoading: false memoLoading: false
@ -116,20 +118,20 @@ class MemoDetail extends Component {
$('body>#root').off('onMemoDelete') $('body>#root').off('onMemoDelete')
} }
onMemoDelete(memo) { onMemoDelete(memo) {
const deleteUrl = `/api/v1/memos/${memo.id}`; const deleteUrl = `/memos/${memo.id}.json`;
// 获取memo list // 获取memo list
axios.delete(deleteUrl, { axios.delete(deleteUrl, {
withCredentials: true, // withCredentials: true,
}) })
.then((response) => { .then((response) => {
const status = response.data.status const status = response.data.status
if (status === 0) { if (status === 0) {
this.props.showSnackbar('删除成功'); this.props.showNotification('删除成功');
this.props.history.push(`/forums`) this.props.history.push(`/forums`)
} else if (status === -1) { } else if (status === -1) {
this.props.showSnackbar('帖子已被删除'); this.props.showNotification('帖子已被删除');
this.props.history.push(`/forums`) this.props.history.push(`/forums`)
} }
}).catch((error) => { }).catch((error) => {
@ -158,14 +160,14 @@ class MemoDetail extends Component {
clickPraise(){ clickPraise(){
const { memo } = this.props; const { memo } = this.props;
const url = `/api/v1/discusses/${memo.id}/plus`; const url = `/discusses/${memo.id}/plus.json`;
console.log(url) console.log(url)
axios.post(url, { axios.post(url, {
container_type: 'Memo', container_type: 'Memo',
type: 1 // "踩0赞1" type: 1 // "踩0赞1"
}, },
{ {
withCredentials: true // withCredentials: true
} }
).then((response) => { ).then((response) => {
console.log(response); console.log(response);
@ -178,18 +180,16 @@ class MemoDetail extends Component {
}) })
} }
renderAttachment() { renderAttachment() {
const { memo } = this.props; const { memo, attachments_list } = this.props;
const attachments = [] const attachments = []
memo.attachment_url.forEach((item, index) => { attachments_list.forEach((item, index) => {
const ar = item.url.split('/') const ar = item.url.split('/')
const fileName = item.filename || ar[ar.length - 1] const fileName = item.title
let filesize = 0 let filesize = item.filesize
if (item.filesize) {
filesize = bytesToSize(item.filesize)
}
attachments.push( attachments.push(
<p className="clearfix" key={index} > <p className="clearfix" key={index} >
<a href={item.url} className="color-green clearfix notefileDownload"> <a href={item.url} className="color-green clearfix notefileDownload" target="_blank">
<i className="iconfont icon-xiazai color-green ml5 fl"></i><span className="fl">{fileName}{filesize? ` ${filesize}` : ''}</span> <i className="iconfont icon-xiazai color-green ml5 fl"></i><span className="fl">{fileName}{filesize? ` ${filesize}` : ''}</span>
</a> </a>
</p> </p>
@ -213,9 +213,9 @@ class MemoDetail extends Component {
} }
} }
replyComment = (commentContent, id, editor) => { replyComment = (commentContent, id, editor) => {
const { showSnackbar } = this.props; const { showNotification } = this.props;
if (!commentContent || commentContent.length === 0) { if (!commentContent || commentContent.length === 0) {
showSnackbar('必须填写内容!') showNotification('必须填写内容!')
return; return;
} }
@ -223,7 +223,8 @@ class MemoDetail extends Component {
this.createNewComment(commentContent, id, editor); this.createNewComment(commentContent, id, editor);
return; return;
} }
const url = `/api/v1/memos/${id}/reply`; // /${id}
const url = `/memos/reply.json`;
const { comments } = this.state; const { comments } = this.state;
const user = this._getUser(); const user = this._getUser();
/* /*
@ -235,12 +236,14 @@ class MemoDetail extends Component {
commentContent = commentContent.replace(/(\n<p>\n\t<br \/>\n<\/p>)*$/g,''); commentContent = commentContent.replace(/(\n<p>\n\t<br \/>\n<\/p>)*$/g,'');
} }
axios.post(url, { axios.post(url, {
parent_id: id,
content: commentContent content: commentContent
}, },
{ {
withCredentials: true // withCredentials: true
} }
).then((response) => { ).then((response) => {
response.data.memo = response.data
if (response.data.memo) { if (response.data.memo) {
let newDiscuss = response.data.memo; let newDiscuss = response.data.memo;
@ -303,12 +306,12 @@ class MemoDetail extends Component {
if (childCommentId) { if (childCommentId) {
deleteCommentId = childCommentId; deleteCommentId = childCommentId;
} }
const url = `/api/v1/memos/${deleteCommentId}` const url = `/memos/${deleteCommentId}.json`
let comments = this.state.comments; let comments = this.state.comments;
axios.delete(url, axios.delete(url,
{ {
withCredentials: true // withCredentials: true
} }
).then((response) => { ).then((response) => {
// TODO 删除成功或失败 // TODO 删除成功或失败
@ -346,7 +349,7 @@ class MemoDetail extends Component {
const { comments } = this.state; const { comments } = this.state;
const commentIndex = this._findById(discussId, comments); const commentIndex = this._findById(discussId, comments);
const url = `/api/v1/discusses/${discussId}/plus` const url = `/discusses/${discussId}/plus.json`
axios.post(url, { axios.post(url, {
// id: discussId, // id: discussId,
// container_id: challenge.id, // container_id: challenge.id,
@ -354,7 +357,7 @@ class MemoDetail extends Component {
type: comments[commentIndex].user_praise === true ? 0 : 1, // "踩0赞1" type: comments[commentIndex].user_praise === true ? 0 : 1, // "踩0赞1"
}, },
{ {
withCredentials: true // withCredentials: true
} }
).then((response) => { ).then((response) => {
if (response.data.praise_count === 0 || response.data.praise_count) { if (response.data.praise_count === 0 || response.data.praise_count) {
@ -372,14 +375,14 @@ class MemoDetail extends Component {
}) })
} }
rewardCode = (parrentComment, childComment, amount) => { rewardCode = (parrentComment, childComment, amount) => {
const { showSnackbar } = this.props; const { showNotification } = this.props;
const { comments } = this.state; const { comments } = this.state;
let handleComment = parrentComment let handleComment = parrentComment
if (childComment) { if (childComment) {
handleComment = childComment; handleComment = childComment;
} }
let handleCommentId = handleComment.id; let handleCommentId = handleComment.id;
const url = `/api/v1/discusses/${handleCommentId}/reward_code` const url = `/discusses/${handleCommentId}/reward_code.json`
axios.post(url, { axios.post(url, {
id: handleCommentId, id: handleCommentId,
@ -389,7 +392,7 @@ class MemoDetail extends Component {
user_id: handleComment.user_id user_id: handleComment.user_id
}, },
{ {
withCredentials: true // withCredentials: true
} }
).then((response) => { ).then((response) => {
if (response.data && response.data.code) { if (response.data && response.data.code) {
@ -416,14 +419,14 @@ class MemoDetail extends Component {
} }
}).catch((error) => { }).catch((error) => {
console.log(error) console.log(error)
showSnackbar('奖励失败,请联系系统管理员!') showNotification('奖励失败,请联系系统管理员!')
}) })
} }
hiddenComment = (item, childCommentId) => { hiddenComment = (item, childCommentId) => {
const id = item.id const id = item.id
const { showSnackbar } = this.props; const { showNotification } = this.props;
const user = this._getUser(); const user = this._getUser();
const url = `/api/v1/memos/${id}/hidden` const url = `/memos/${id}/hidden.json`
const { comments } = this.state; const { comments } = this.state;
const commentIndex = this._findById(id, comments); const commentIndex = this._findById(id, comments);
@ -432,14 +435,14 @@ class MemoDetail extends Component {
hidden: !comment.hidden ? "1" : "0" hidden: !comment.hidden ? "1" : "0"
}, },
{ {
withCredentials: true // withCredentials: true
} }
).then((response) => { ).then((response) => {
if (response.data.status === -1) { if (response.data.status === -1) {
showSnackbar(response.data.message) showNotification(response.data.message)
return; return;
} }
if (response.data.status === 1) { if (response.data.status === 0) {
if (!childCommentId) { if (!childCommentId) {
comment.hidden = !comment.hidden; comment.hidden = !comment.hidden;
@ -473,14 +476,16 @@ class MemoDetail extends Component {
return; return;
} }
} }
const url = `/api/v1/memos/${memo.id}/reply`; // /${memo.id}
const url = `/memos/reply.json`;
let { comments } = this.state; let { comments } = this.state;
const user = this._getUser(); const user = this._getUser();
axios.post(url, { axios.post(url, {
parent_id: memo.id,
content: content content: content
}, },
{ {
withCredentials: true // withCredentials: true
} }
).then((response) => { ).then((response) => {
if (response.data.status === -1) { if (response.data.status === -1) {
@ -488,6 +493,7 @@ class MemoDetail extends Component {
return; return;
} }
if (response.data) { if (response.data) {
response.data.memo = response.data
const newMemo = response.data.memo; const newMemo = response.data.memo;
// ke // ke
editor.html && editor.html(''); editor.html && editor.html('');
@ -536,12 +542,12 @@ class MemoDetail extends Component {
let { comments, pageCount } = this.state; let { comments, pageCount } = this.state;
let { memo } = this.props; let { memo } = this.props;
const user = this._getUser(); const user = this._getUser();
const url = `/api/v1/memos/${memo.id}/more_reply?page=${pageCount}`; const url = `/memos/${memo.id}/more_reply.json?page=${pageCount}`;
axios.get(url, { axios.get(url, {
}, },
{ {
withCredentials: true // withCredentials: true
} }
).then((response) => { ).then((response) => {
if (response.data.status === -1) { if (response.data.status === -1) {
@ -584,15 +590,15 @@ class MemoDetail extends Component {
params.forum_id = this.state.p_forum_id; params.forum_id = this.state.p_forum_id;
} }
let paramsUrl = urlStringify(params) let paramsUrl = urlStringify(params)
const set_top_or_down_Url = `/api/v1/memos/${memo.id}/set-top-or-down?${paramsUrl}`; const set_top_or_down_Url = `/memos/${memo.id}/sticky_or_cancel.json?${paramsUrl}`;
// 获取memo list // 获取memo list
axios.get(set_top_or_down_Url, { axios.post(set_top_or_down_Url, {
withCredentials: true, // withCredentials: true,
}) })
.then((response) => { .then((response) => {
const status = response.data.status const status = response.data.status
if (status === 0) { if (status === 0) {
this.props.showSnackbar( memo.sticky ? '取消置顶成功' : '置顶成功'); this.props.showNotification( memo.sticky ? '取消置顶成功' : '置顶成功');
memo.sticky = memo.sticky ? false : true memo.sticky = memo.sticky ? false : true
this.setState({ this.setState({
memo: Object.assign({}, memo) memo: Object.assign({}, memo)
@ -608,9 +614,8 @@ class MemoDetail extends Component {
const { memo, author_info } = this.props; const { memo, author_info } = this.props;
const newMemo = Object.assign({}, memo); const newMemo = Object.assign({}, memo);
const _reward = parseInt(inputVal) const _reward = parseInt(inputVal)
const newMemoUrl = `/api/v1/memos/${memo.id}/update`
const url = `/api/v1/discusses/${memo.id}/reward_code` const url = `/discusses/${memo.id}/reward_code.json`
axios.post(url, { axios.post(url, {
id: memo.id, id: memo.id,
@ -619,7 +624,7 @@ class MemoDetail extends Component {
score: _reward, score: _reward,
user_id: author_info.user_id user_id: author_info.user_id
}, { }, {
withCredentials: true, // withCredentials: true,
}) })
.then((response) => { .then((response) => {
const { code } = response.data; const { code } = response.data;
@ -628,9 +633,9 @@ class MemoDetail extends Component {
this.props.initForumState({ this.props.initForumState({
memo: newMemo memo: newMemo
}) })
this.props.showSnackbar( '奖励成功' ); this.props.showNotification( '奖励成功' );
} else { } else {
this.props.showSnackbar( '奖励失败,请联系系统管理员!' ); this.props.showNotification( '奖励失败,请联系系统管理员!' );
} }
}).catch((error) => { }).catch((error) => {
console.log(error) console.log(error)
@ -659,14 +664,24 @@ class MemoDetail extends Component {
const { memo, recommend_shixun, current_user,author_info } = this.props; const { memo, recommend_shixun, current_user,author_info } = this.props;
const { comments, hasMoreComments, goldRewardDialogOpen } = this.state; const { comments, hasMoreComments, goldRewardDialogOpen } = this.state;
if (this.state.memoLoading) { if (!memo || this.state.memoLoading) {
return <div className="edu-back-white" id="forum_index_list"></div> return <div className="edu-back-white" id="forum_index_list"></div>
} }
current_user.user_url = `/users/${current_user.login}`; let _current_user = {}
if (current_user) {
_current_user = current_user
}
(_current_user.user_url = `/users/${_current_user.login}`);
memo.isDetailPage = true; memo.isDetailPage = true;
// TODO 图片上传地址 // TODO 图片上传地址
return ( return (
<div className="edu-back-white" id="forum_index_list"> {/* fl with100 */} <div className="edu-back-white memoDetail" id="forum_index_list"> {/* fl with100 */}
<style>{`
.memoDetail .commentsbtn {
margin-top: 6px;
}
`}</style>
<RewardDialog goldRewardDialogOpen={goldRewardDialogOpen} <RewardDialog goldRewardDialogOpen={goldRewardDialogOpen}
setRewardDialogVisible={this.setRewardDialogVisible} setRewardDialogVisible={this.setRewardDialogVisible}
rewardCode={this.rewardCodeMemo} rewardCode={this.rewardCodeMemo}
@ -674,42 +689,52 @@ class MemoDetail extends Component {
<div className="clearfix"> <div className="clearfix">
<div id="forum_list" className="forum_table mh650"> <div id="forum_list" className="forum_table mh650">
<div className="padding40-30 bor-bottom-greyE"> <div className="padding40-30 bor-bottom-greyE">
<div className="font-16 mb5 cdefault clearfix pr pr35"> <div className="font-16 mb5 cdefault clearfix pr pr35" style={{display: 'flex', alignItems: 'center'}}>
<span className="noteDetailTitle">{memo.subject}</span> <span className="noteDetailTitle">{memo.subject}</span>
{ memo.sticky && <span className="btn-cir btn-cir-red ml10 mt10">置顶</span>} { memo.sticky && <span className="btn-cir btn-cir-red ml10 " style={{ height: '20px' }}>置顶</span>}
{ !!memo.reward && <span className="color-orange font-14 ml15" { !!memo.reward && <span className="color-orange font-14 ml15"
data-tip-down={`获得平台奖励金币:${memo.reward}`} > data-tip-down={`获得平台奖励金币:${memo.reward}`} >
<i className="iconfont icon-gift mr5"></i>{memo.reward} <i className="iconfont icon-gift mr5"></i>{memo.reward}
</span> } </span> }
{ current_user && (current_user.admin === true || current_user.user_id === author_info.user_id) &&
<div className="edu-position-hidebox" style={{position: 'absolute', right: '18px',top:'4px'}}> <div style={{ flex: 1 }}>
<a href="javascript:void(0);"><i className="fa fa-bars font-16"></i></a> { _current_user && (_current_user.admin === true || _current_user.user_id === author_info.user_id) &&
<ul className="edu-position-hide undis"> <div className="edu-position-hidebox" style={{position: 'absolute', right: '12px',top:'4px'}}>
{ current_user.admin === true && <a href="javascript:void(0);"><i className="fa fa-bars font-16"></i></a>
( memo.sticky === true ? <ul className="edu-position-hide undis">
<li><a href="javascript:void(0);" onClick={() => this.setTop(memo)}>取消置顶</a></li> { _current_user.admin === true &&
: ( memo.sticky === true ?
<li><a href="javascript:void(0);" onClick={() => this.setTop(memo)}>&nbsp;&nbsp;</a></li> ) <li><a href="javascript:void(0);" onClick={() => this.setTop(memo)}>取消置顶</a></li>
} :
<li><Link to={`/forums/${memo.id}/edit`}>&nbsp;&nbsp;</Link></li> <li><a href="javascript:void(0);" onClick={() => this.setTop(memo)}>&nbsp;&nbsp;</a></li> )
<li> }
<a href="javascript:void(0)" onClick={() => <li><Link to={`/forums/${memo.id}/edit`}>&nbsp;&nbsp;</Link></li>
window.delete_confirm_box_2_react(`onMemoDelete`, '您确定要删除吗?' , memo)}> <li>
<a href="javascript:void(0)" onClick={() =>
&nbsp;&nbsp;</a> window.delete_confirm_box_2_react(`onMemoDelete`, '您确定要删除吗?' , memo)}>
</li>
</ul> &nbsp;&nbsp;</a>
</div> </li>
} </ul>
<Link className={`task-hide fr return_btn color-grey-6 mt2 ${ current_user && (current_user.admin === true </div>
|| current_user.user_id === author_info.user_id) ? '': 'no_mr'} `} to="/forums" > }
返回 <Link className={`task-hide fr return_btn color-grey-6 mt2 ${ _current_user && (_current_user.admin === true
</Link> || _current_user.user_id === author_info.user_id) ? '': 'no_mr'} `} to="/forums"
style={{ marginRight: '10px'}}
>
返回
</Link>
</div>
</div> </div>
<div className="color-grey-9 clearfix"> <div className="color-grey-9 clearfix">
<span className="fl">{moment(memo.time).fromNow()} 发布</span> <span className="fl">{moment(memo.time).fromNow()} 发布</span>
<div className="fr"> <div className="fr detailIcons">
{ current_user.admin && <Tooltip title={ "帖子奖励" }> <style>{`
.detailIcons i{
vertical-align: sub;
}
`}</style>
{ _current_user.admin && <Tooltip title={ "帖子奖励" }>
<span className="noteDetailNum rightline cdefault" style={{padding: '0 4px', cursor: 'pointer'}}> <span className="noteDetailNum rightline cdefault" style={{padding: '0 4px', cursor: 'pointer'}}>
<i className="iconfont icon-jiangli mr5" onClick={this.showRewardDialog}></i> <i className="iconfont icon-jiangli mr5" onClick={this.showRewardDialog}></i>
</span> </span>
@ -750,7 +775,7 @@ class MemoDetail extends Component {
</p> </p>
</div> </div>
{ memo.attachment_url && !!memo.attachment_url.length && { this.props.attachments_list &&
<div> <div>
{this.renderAttachment()} {this.renderAttachment()}
</div> </div>
@ -770,7 +795,7 @@ class MemoDetail extends Component {
<span className="count">{memo.replies_count}</span> <span className="count">{memo.replies_count}</span>
</div> </div>
<Comments comments={comments} user={current_user} <Comments comments={comments} user={_current_user}
replyComment={this.replyComment} replyComment={this.replyComment}
deleteComment={this.deleteComment} deleteComment={this.deleteComment}
commentPraise={this.commentPraise} commentPraise={this.commentPraise}
@ -780,7 +805,7 @@ class MemoDetail extends Component {
></Comments> ></Comments>
{ hasMoreComments ? { hasMoreComments ?
<div className="memoMore"> <div className="memoMore" style={{ cursor: 'default' }}>
<a onClick={this.moreMemos}>查看更多评论</a> <a onClick={this.moreMemos}>查看更多评论</a>
<div className="writeCommentBtn" onClick={this.showCommentInput}>写评论</div> <div className="writeCommentBtn" onClick={this.showCommentInput}>写评论</div>
</div> </div>

@ -42,7 +42,7 @@ class MemoDetailMDEditor extends Component {
window.__tt = 400; window.__tt = 400;
setTimeout(() => { setTimeout(() => {
var commentMDEditor = window.create_editorMD_4comment("memo_comment_editorMd", '', this.props.height || 240, placeholder, imageUrl, () => { var commentMDEditor = window.create_editorMD_4comment("memo_comment_editorMd", '', this.props.height || 240, placeholder, imageUrl, () => {
commentMDEditor.focus() // commentMDEditor.focus()
this.isMDInited = true this.isMDInited = true
this.initDrag() this.initDrag()

@ -1,173 +1,173 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Redirect } from 'react-router'; import { Redirect } from 'react-router';
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom"; import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classNames from 'classnames' import classNames from 'classnames'
import { isDev } from 'educoder' import { isDev } from 'educoder'
import './MemoDetailEditor.css' import './MemoDetailEditor.css'
require('codemirror/lib/codemirror.css'); require('codemirror/lib/codemirror.css');
const $ = window.$; const $ = window.$;
///作业回答 专用 ///作业回答 专用
class MemoDetailMDEditortwo extends Component { class MemoDetailMDEditortwo extends Component {
constructor(props) { constructor(props) {
super(props) super(props)
this.state = { this.state = {
isInited: this.props.usingMockInput ? false : true, isInited: this.props.usingMockInput ? false : true,
isError: false, isError: false,
errorMsg: '' errorMsg: ''
} }
} }
componentDidUpdate(prevProps, prevState, snapshot) { componentDidUpdate(prevProps, prevState, snapshot) {
if (this.props.memo && (!prevProps.memo || this.props.memo.id != prevProps.memo.id)) { if (this.props.memo && (!prevProps.memo || this.props.memo.id != prevProps.memo.id)) {
// this.keEditor = window.sd_create_editor_from_data(this.props.memo.id, null, "100%", "Memo"); // this.keEditor = window.sd_create_editor_from_data(this.props.memo.id, null, "100%", "Memo");
// window._kk = this.keEditor // window._kk = this.keEditor
} }
} }
initMDEditor = () => { initMDEditor = () => {
// 因为props.memo不存在时本组件不会被加载这里直接在didMount里初始化即可 // 因为props.memo不存在时本组件不会被加载这里直接在didMount里初始化即可
const placeholder = '我要回复...' const placeholder = '我要回复...'
// const imageUrl = `/upload_with_markdown?container_id=${this.props.memo.id}&container_type=Memo`; // const imageUrl = `/upload_with_markdown?container_id=${this.props.memo.id}&container_type=Memo`;
const imageUrl = `/api/attachments.json`; const imageUrl = `/api/attachments.json`;
// 执行太快了,样式不正常 // 执行太快了,样式不正常
window.__tt = 400; window.__tt = 400;
setTimeout(() => { setTimeout(() => {
var commentMDEditor = window.create_editorMD_4comment("memo_comment_editorMd", '', this.props.height || 240, placeholder, imageUrl, () => { var commentMDEditor = window.create_editorMD_4comment("memo_comment_editorMd", '', this.props.height || 240, placeholder, imageUrl, () => {
commentMDEditor.focus() commentMDEditor.focus();
this.initDrag() this.initDrag();
commentMDEditor.cm.on("change", (_cm, changeObj) => { commentMDEditor.cm.on("change", (_cm, changeObj) => {
this.setState({ this.setState({
isError: false, isError: false,
errorMsg: '' errorMsg: ''
}) })
}) })
}, { }, {
watch: false, watch: false,
dialogLockScreen: false, dialogLockScreen: false,
}); });
this.commentMDEditor = commentMDEditor; this.commentMDEditor = commentMDEditor;
window.commentMDEditor = commentMDEditor; window.commentMDEditor = commentMDEditor;
}, window.__tt) }, window.__tt)
} };
componentDidMount() { componentDidMount() {
!this.props.usingMockInput && this.initMDEditor() !this.props.usingMockInput && this.initMDEditor()
} }
initDrag = () => { initDrag = () => {
window.initMDEditorDragResize(".editor__resize", this.commentMDEditor) window.initMDEditorDragResize(".editor__resize", this.commentMDEditor)
} }
onCommit = () => { onCommit = () => {
const content = this.commentMDEditor.getValue(); const content = this.commentMDEditor.getValue();
// this.props.showError == // this.props.showError ==
if (this.props.showError == true) { if (this.props.showError == true) {
if (!content || content.trim() == "") { if (!content || content.trim() == "") {
this.setState({ this.setState({
isError: true, isError: true,
errorMsg: '不能为空' errorMsg: '不能为空'
}) })
return; return;
} else if (content.length > 2000) { } else if (content.length > 2000) {
this.setState({ this.setState({
isError: true, isError: true,
errorMsg: '不能超过2000个字符' errorMsg: '不能超过2000个字符'
}) })
return; return;
} }
this.setState({ this.setState({
isError: false, isError: false,
errorMsg: '' errorMsg: ''
}) })
} }
window.$(document).trigger("onReply", { commentContent: content window.$(document).trigger("onReply", { commentContent: content
, id: this.props.memo.id, editor: this.commentMDEditor } ); , id: this.props.memo.id, editor: this.commentMDEditor } );
} }
showEditor() { showEditor() {
$("html, body").animate({ scrollTop: $('#commentInput').offset().top - 100 }, 1000, () => { $("html, body").animate({ scrollTop: $('#commentInput').offset().top - 100 }, 1000, () => {
if (this.commentMDEditor) { if (this.commentMDEditor) {
this.commentMDEditor.cm.focus() this.commentMDEditor.cm.focus()
} else { } else {
$('#commentInput input')[0].click() $('#commentInput input')[0].click()
} }
}); });
} }
onMockInputClick = () => { onMockInputClick = () => {
this.setState({isInited: true}) this.setState({isInited: true})
this.initMDEditor() this.initMDEditor()
} }
render() { render() {
const { match, history, memo, placeholder } = this.props const { match, history, memo, placeholder } = this.props
const { isInited, errorMsg } = this.state const { isInited, errorMsg } = this.state
if (!memo) { if (!memo) {
return <div></div> return <div></div>
} }
return ( return (
<React.Fragment> <React.Fragment>
<style>{` <style>{`
.mockInputWrapper { .mockInputWrapper {
display: flex; display: flex;
padding: 30px; padding: 30px;
} }
.mockInputWrapper input { .mockInputWrapper input {
flex:1; flex:1;
padding-left: 10px; padding-left: 10px;
height: 40px; height: 40px;
background: rgb(246,246,246); background: rgb(246,246,246);
margin-right: 20px; margin-right: 20px;
} }
.mockInputWrapper a.commentsbtn { .mockInputWrapper a.commentsbtn {
height: 40px; height: 40px;
display: inline-block; display: inline-block;
margin-top: 0px !important; margin-top: 0px !important;
vertical-align: text-top; vertical-align: text-top;
padding-top: 6px; padding-top: 6px;
width: 60px; width: 60px;
margin-right: 0px !important; margin-right: 0px !important;
} }
#commentInput .editormd{ #commentInput .editormd{
width:100%!important; width:100%!important;
} }
`}</style> `}</style>
<div style={{ display: isInited ? 'none' : ''}} className="mockInputWrapper" id="commentInput"> <div style={{ display: isInited ? 'none' : ''}} className="mockInputWrapper" id="commentInput">
<input onClick={this.onMockInputClick} placeholder={placeholder || '我要回复'}></input> <input onClick={this.onMockInputClick} placeholder={placeholder || '我要回复'}></input>
<a href="javascript:void(0)" <a href="javascript:void(0)"
onClick={this.onMockInputClick} className="commentsbtn task-btn task-btn-blue"> onClick={this.onMockInputClick} className="commentsbtn task-btn task-btn-blue">
发送 发送
</a> </a>
</div> </div>
<div nhname={`new_message_${memo.id}`} className="" <div nhname={`new_message_${memo.id}`} className=""
style={{ padding: '30px',boxSizing:"border-box", display: isInited ? '' : 'none' }} id="commentInput"> style={{ padding: '30px',boxSizing:"border-box", display: isInited ? '' : 'none' }} id="commentInput">
<div id="memo_comment_editorMd" className="editorMD" style={{ marginBottom: '0px' <div id="memo_comment_editorMd" className="editorMD" style={{ marginBottom: '0px'
, border: errorMsg ? '1px solid red' : '1px solid #ddd'}}> , border: errorMsg ? '1px solid red' : '1px solid #ddd'}}>
<textarea style={{'display': 'none'}}> <textarea style={{'display': 'none'}}>
</textarea> </textarea>
</div> </div>
<div className="editor__resize" href="javascript:void(0);">调整高度</div> <div className="editor__resize" href="javascript:void(0);">调整高度</div>
{ errorMsg && <input className="fl" style={{color: 'red', marginTop: '6px', { errorMsg && <input className="fl" style={{color: 'red', marginTop: '6px',
marginLeft: '4px'}}>{errorMsg}</input> } marginLeft: '4px'}}>{errorMsg}</input> }
<a id={`new_message_submit_btn_${memo.id}`} href="javascript:void(0)" <a id={`new_message_submit_btn_${memo.id}`} href="javascript:void(0)"
onClick={this.onCommit} className="commentsbtn task-btn-blue task-btn fr "> onClick={this.onCommit} className="commentsbtn task-btn-blue task-btn fr ">
发送 发送
</a> </a>
</div> </div>
</React.Fragment> </React.Fragment>
); );
} }
} }
export default ( MemoDetailMDEditortwo ); export default ( MemoDetailMDEditortwo );

@ -7,16 +7,17 @@ import PropTypes from 'prop-types';
import classNames from 'classnames' import classNames from 'classnames'
import { Select,Icon } from 'antd'; import { Select,Icon, Upload, Button } from 'antd';
// demo http://react-component.github.io/upload/examples/simple.html // demo http://react-component.github.io/upload/examples/simple.html
import Upload from 'rc-upload'; // import Upload from 'rc-upload';
import axios from 'axios' import axios from 'axios'
import 'antd/lib/select/style/index.css' import 'antd/lib/select/style/index.css'
import TPMMDEditor from '../tpm/challengesnew/TPMMDEditor'
import { getUrl } from 'educoder' import { getUrl, getUploadActionUrl, appendFileSizeToUploadFileAll, appendFileSizeToUploadFile } from 'educoder'
const Option = Select.Option; const Option = Select.Option;
const $ = window.$; const $ = window.$;
@ -130,6 +131,7 @@ const languageSeparator = '/'
class MemoNew extends Component { class MemoNew extends Component {
constructor(props) { constructor(props) {
super(props) super(props)
this.mdRef = React.createRef();
// https://testbdweb.trustie.net/uploads.js?attachment_id=1&filename=jqui.js // https://testbdweb.trustie.net/uploads.js?attachment_id=1&filename=jqui.js
// https://ant.design/components/upload-cn/ // https://ant.design/components/upload-cn/
@ -167,32 +169,34 @@ class MemoNew extends Component {
repertoires: [], repertoires: [],
currentSelectRepertoiresIndex: -1, currentSelectRepertoiresIndex: -1,
repertoiresTagMap: {} repertoiresTagMap: {},
fileList: []
} }
} }
onCommit() { onCommit() {
const { memoSubject, memoRepertoire, memoLanguage, currentMemoId, memoType } = this.state; const { memoSubject, memoRepertoire, memoLanguage, currentMemoId, memoType } = this.state;
const { showSnackbar } = this.props; const { showNotification } = this.props;
if (!memoSubject) { if (!memoSubject) {
showSnackbar('请先输入话题名称') showNotification('请先输入话题名称')
return return
} }
let mdVal; let mdVal;
try { try {
mdVal = this.taskpass_editormd.getValue() mdVal = this.mdRef.current.getValue()
} catch (e) { } catch (e) {
showSnackbar('编辑器还未加载完毕,请稍后') showNotification('编辑器还未加载完毕,请稍后')
return return
} }
if (!mdVal) { if (!mdVal) {
showSnackbar('请先输入话题内容') showNotification('请先输入话题内容')
return return
} }
// !memoRepertoire || // !memoRepertoire ||
if (memoType === 5 && ( !memoLanguage || memoLanguage.length === 0 )) { if (memoType === 5 && ( !memoLanguage || memoLanguage.length === 0 )) {
showSnackbar('请先选择技术标签') showNotification('请先选择技术标签')
return return
} }
/* /*
@ -205,23 +209,27 @@ class MemoNew extends Component {
// collect attachments // collect attachments
const $ = window.$; const $ = window.$;
const attachmentsMap = {}; const attachmentsMap = {};
$('#attachments_fields .attachment').each(( index, item ) => { const attachmentIds = this.state.fileList.map(item => {
const filename = $(item).find('.upload_filename').val(); return item.response ? item.response.id : item.id
// $($('#attachments_fields .attachment')[0]).find('input:nth-child(6)').val()
const token = $(item).find('input:nth-child(7)').val()
const attachment_id = parseInt($(item).children().last().val())
attachmentsMap[index] = {
filename,
token,
attachment_id
}
}) })
// $('#attachments_fields .attachment').each(( index, item ) => {
// const filename = $(item).find('.upload_filename').val();
// // $($('#attachments_fields .attachment')[0]).find('input:nth-child(6)').val()
// const token = $(item).find('input:nth-child(7)').val()
// const attachment_id = parseInt($(item).children().last().val())
// attachmentsMap[index] = {
// filename,
// token,
// attachment_id
// }
// attachmentIds.push(attachment_id)
// })
if (currentMemoId) { if (currentMemoId) {
this.updateMemo(attachmentsMap) this.updateMemo(attachmentIds)
} else { } else {
this.newMemo(attachmentsMap) this.newMemo(attachmentIds)
} }
} }
onCancel() { onCancel() {
@ -235,23 +243,23 @@ class MemoNew extends Component {
} }
updateMemo(attachmentsMap) { updateMemo(attachmentsMap) {
const { memoSubject, memoRepertoire, memoLanguage, memoType, currentMemoId, content } = this.state; const { memoSubject, memoRepertoire, memoLanguage, memoType, currentMemoId, content } = this.state;
const mdVal = this.taskpass_editormd.getValue() const mdVal = this.mdRef.current.getValue()
console.log('isContentEdit: ', mdVal === content); console.log('isContentEdit: ', mdVal === content);
const newMemoUrl = `/api/v1/memos/${currentMemoId}/update` const newMemoUrl = `/memos/${currentMemoId}.json`
axios.post(newMemoUrl, { axios.put(newMemoUrl, {
content_changed: this.contentChanged, content_changed: this.contentChanged,
tags: memoLanguage, tags: memoLanguage,
memo:{ // memo:{
subject: memoSubject , subject: memoSubject ,
content: mdVal, content: mdVal,
forum_id: memoType, forum_id: memoType,
repertoire_name: memoRepertoire, repertoire_name: memoRepertoire,
// language: memoLanguage.join(languageSeparator), // language: memoLanguage.join(languageSeparator),
// //
}, // },
attachments: attachmentsMap attachment_ids: attachmentsMap
}, { }, {
withCredentials: true, // withCredentials: true,
}) })
.then((response) => { .then((response) => {
const { status, message, memo_id } = response.data; const { status, message, memo_id } = response.data;
@ -259,7 +267,7 @@ class MemoNew extends Component {
window.$("html,body").animate({"scrollTop":0}) window.$("html,body").animate({"scrollTop":0})
this.props.history.push(`/forums/${currentMemoId}`) this.props.history.push(`/forums/${currentMemoId}`)
} else { } else {
this.props.showSnackbar(message) this.props.showNotification(message)
} }
}).catch((error) => { }).catch((error) => {
console.log(error) console.log(error)
@ -267,21 +275,21 @@ class MemoNew extends Component {
} }
newMemo(attachmentsMap) { newMemo(attachmentsMap) {
const { memoSubject, memoRepertoire, memoLanguage, memoType } = this.state; const { memoSubject, memoRepertoire, memoLanguage, memoType } = this.state;
const mdVal = this.taskpass_editormd.getValue() const mdVal = this.mdRef.current.getValue()
const newMemoUrl = `/api/v1/memos/create` const newMemoUrl = `/memos.json`
axios.post(newMemoUrl, { axios.post(newMemoUrl, {
tags: memoLanguage, tags: memoLanguage,
memo:{ // memo:{
subject: memoSubject , subject: memoSubject ,
content: mdVal, content: mdVal,
forum_id: memoType, forum_id: memoType,
repertoire_name: memoRepertoire, // repertoire_name: memoRepertoire,
}, // },
attachments: attachmentsMap attachment_ids: attachmentsMap
}, { }, {
withCredentials: true, // withCredentials: true,
}) })
.then((response) => { .then((response) => {
const { status, message, memo_id } = response.data; const { status, message, memo_id } = response.data;
@ -289,16 +297,16 @@ class MemoNew extends Component {
window.$("html,body").animate({"scrollTop":0}) window.$("html,body").animate({"scrollTop":0})
this.props.history.push(`/forums/${memo_id}`) this.props.history.push(`/forums/${memo_id}`)
} else { } else {
this.props.showSnackbar(message) this.props.showNotification(message)
} }
}).catch((error) => { }).catch((error) => {
console.log(error) console.log(error)
}) })
} }
componentDidMount() { componentDidMount() {
const newMemoUrl = `/api/v1/memos/new` const newMemoUrl = `/memos/new.json`
axios.get(newMemoUrl,{ axios.get(newMemoUrl,{
withCredentials: true, // withCredentials: true,
}) })
.then((response) => { .then((response) => {
const data = response.data; const data = response.data;
@ -319,9 +327,9 @@ class MemoNew extends Component {
// repertoiresTagMap // repertoiresTagMap
}) })
const user = response.data.current_user; // const user = response.data.current_user;
user.tidding_count = response.data.tidding_count; // user.tidding_count = response.data.tidding_count;
this.props.initCommonState(user) // this.props.initCommonState(user)
// 初始化 csrf meta // 初始化 csrf meta
const $ = window.$ const $ = window.$
@ -336,26 +344,39 @@ class MemoNew extends Component {
const { match } = this.props const { match } = this.props
const memoId = match.params.memoId; const memoId = match.params.memoId;
if (memoId) { if (memoId) {
const memoUrl = `/api/v1/memos/${match.params.memoId}/edit`; const memoUrl = `/memos/${match.params.memoId}/edit.json`;
axios.get(memoUrl,{ axios.get(memoUrl,{
withCredentials: true, // withCredentials: true,
}) })
.then((response) => { .then((response) => {
const tag_list = response.data.tag_list const tag_list = response.data.tag_list
if (tag_list) { if (tag_list) {
// this.setState({...response.data}) // this.setState({...response.data})
const { content, forum_id, id, tag, repertoire_name, subject, const { content, forum_id, id, repertoire_name, subject,
current_user, tag_list, attachments_url } = response.data; current_user, tag_list, attachments_url, memo_tags, attachments } = response.data;
this.initMD(content); this.initMD(content);
// this.onRepertoiresChange(repertoire_name) // this.onRepertoiresChange(repertoire_name)
// tag -> memo_tags
const tag = memo_tags;
let memoLanguage = [] let memoLanguage = []
if (tag) { if (tag) {
memoLanguage = tag.map((item, index) => { memoLanguage = tag.map((item, index) => {
return item.id + "" return item.id + ""
}) })
} }
const fileList = attachments.map(item => {
return {
id: item.id,
uid: item.id,
name: appendFileSizeToUploadFile(item),
url: item.url,
filesize: item.filesize,
status: 'done'
}
})
this.setState({ this.setState({
currentMemoId: id, fileList,
currentMemoId: memoId,
memoSubject: subject, memoSubject: subject,
memoType: forum_id, memoType: forum_id,
memoRepertoire: repertoire_name, memoRepertoire: repertoire_name,
@ -377,7 +398,7 @@ class MemoNew extends Component {
window.$("html,body").animate({"scrollTop":0}) window.$("html,body").animate({"scrollTop":0})
this.props.initForumState({ this.props.initForumState({
current_user, // current_user,
tag_list tag_list
}) })
} }
@ -394,6 +415,8 @@ class MemoNew extends Component {
} }
initMD(initValue) { initMD(initValue) {
return;
this.contentChanged = false; this.contentChanged = false;
const placeholder = ""; const placeholder = "";
// amp; // amp;
@ -458,7 +481,7 @@ class MemoNew extends Component {
onTagChange(value) { onTagChange(value) {
if (value && value.length > 3) { if (value && value.length > 3) {
this.props.showSnackbar(`最多选择3个技术标签`) this.props.showNotification(`最多选择3个技术标签`)
return; return;
} }
@ -523,6 +546,55 @@ class MemoNew extends Component {
) )
}) })
return attachments; return attachments;
}
handleChange = (info) => {
let fileList = info.fileList;
this.setState({
fileList: appendFileSizeToUploadFileAll(fileList)
});
}
onAttachmentRemove = (file) => {
this.props.confirm({
// title: '确定要删除这个附件吗?',
content: '是否确认删除?',
okText: '确定',
cancelText: '取消',
// content: 'Some descriptions',
onOk: () => {
this.deleteAttachment(file)
},
onCancel() {
console.log('Cancel');
},
});
return false;
}
deleteAttachment = (file) => {
// 初次上传不能直接取uid
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
axios.delete(url, {
})
.then((response) => {
if (response.data) {
const { status } = response.data;
if (status == 0) {
console.log('--- success')
this.setState((state) => {
const index = state.fileList.indexOf(file);
const newFileList = state.fileList.slice();
newFileList.splice(index, 1);
return {
fileList: newFileList,
};
});
}
}
})
.catch(function (error) {
console.log(error);
});
} }
render() { render() {
const { match, history } = this.props const { match, history } = this.props
@ -530,8 +602,28 @@ class MemoNew extends Component {
// repertoires, repertoiresTagMap, currentSelectRepertoiresIndex, memoRepertoire, // repertoires, repertoiresTagMap, currentSelectRepertoiresIndex, memoRepertoire,
tag_list, tag_list,
memoSubject, memoType, memoSubject, memoType,
memoLanguage, attachments_url } = this.state; memoLanguage, attachments_url, fileList } = this.state;
const memoId = match.params.memoId; const memoId = match.params.memoId;
const uploadProps = {
width: 600,
fileList,
multiple: true,
// https://github.com/ant-design/ant-design/issues/15505
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
// showUploadList: false,
action: `${getUploadActionUrl()}`,
onChange: this.handleChange,
onRemove: this.onAttachmentRemove,
beforeUpload: (file) => {
console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 < 150;
if (!isLt150M) {
// message.error('文件大小必须小于150MB!');
}
return isLt150M;
},
};
return ( return (
<div > <div >
<div className="pt20 pl20 pr20 pb20 bor-bottom-greyE clearfix" style={{background: '#fff'}}> <div className="pt20 pl20 pr20 pb20 bor-bottom-greyE clearfix" style={{background: '#fff'}}>
@ -566,9 +658,12 @@ class MemoNew extends Component {
<div className="df"> <div className="df">
<span className="mr30 color-orange pt10">*</span> <span className="mr30 color-orange pt10">*</span>
<div className="flex1 mr20"> <div className="flex1 mr20">
<div className="flex1 break_word show_content_grey new_li" id="memoMD"> <TPMMDEditor ref={this.mdRef} placeholder={''} watch={false}
mdID={'memoMD'} initValue={this.state.content} className="memoMD">
</TPMMDEditor>
{/* <div className="flex1 break_word show_content_grey new_li" id="memoMD">
<textarea style={{'display':'none'}}></textarea> <textarea style={{'display':'none'}}></textarea>
</div> </div> */}
<p id="e_tip_memoNew" className="edu-txt-right color-grey-cd font-12"></p> <p id="e_tip_memoNew" className="edu-txt-right color-grey-cd font-12"></p>
<p id="e_tips_memoNew" className="edu-txt-right color-grey-cd font-12"></p> <p id="e_tips_memoNew" className="edu-txt-right color-grey-cd font-12"></p>
</div> </div>
@ -581,13 +676,13 @@ class MemoNew extends Component {
<form className="newForm"> {/* <form className="newForm">
<span id={`attachments_fields`} className="attachments_fields" <span id={`attachments_fields`} className="attachments_fields"
xmlns="http://www.w3.org/1999/html"> xmlns="http://www.w3.org/1999/html">
{ attachments_url && !!attachments_url.length && { attachments_url && !!attachments_url.length &&
this.renderAttachment() this.renderAttachment()
} }
</span> </span>
<span className="add_attachment"> <span className="add_attachment">
<input className="file_selector" data-are-you-sure="您确定要删除吗?" <input className="file_selector" data-are-you-sure="您确定要删除吗?"
data-delete-all-files="您确定要删除所有文件吗" data-description-placeholder="可选的描述" data-delete-all-files="您确定要删除所有文件吗" data-description-placeholder="可选的描述"
@ -599,19 +694,39 @@ class MemoNew extends Component {
onChange={()=>{debugger;window.addInputFiles( window.$('.file_selector')[0] ) }} onChange={()=>{debugger;window.addInputFiles( window.$('.file_selector')[0] ) }}
style={{'display':'none'}} type="file"> style={{'display':'none'}} type="file">
</input> </input>
</span> </span>
</form> </form>*/}
<style>{`
.memo_upload.upload_1 {
margin-left: 36px;
}
.memo_upload.upload_1 .ant-upload-list {
margin-left: 30px;
}
.memo_upload.upload_1 .ant-upload-list-item-info .anticon-paper-clip {
top: 4px;
}
`}</style>
{/*<Upload {...this.uploaderProps} ref="inner"><a>开始上传</a></Upload>*/} {/*<Upload {...this.uploaderProps} ref="inner"><a>开始上传</a></Upload>*/}
<Upload {...uploadProps} className="upload_1 memo_upload" >
<Button className="uploadBtn">
<Icon type="upload" /> 上传附件
</Button>
(单个文件150M以内)
</Upload>
{/* 请求status 422 */} {/* 请求status 422 */}
<div className="df uploadBtn">
{/* <Icon type="upload" ></Icon> */}
{/* <div className="df uploadBtn">
<a href="javascript:void(0);" className="fl" onClick={()=>window.$('#_file').click()} <a href="javascript:void(0);" className="fl" onClick={()=>window.$('#_file').click()}
data-tip-down="请选择文件上传"> data-tip-down="请选择文件上传">
<i className="fa fa-upload mr5 color-blue"></i> <i className="fa fa-upload mr5 color-blue"></i>
<span className="color-blue"> <Icon type="upload" > 上传附件</Icon></span><span style={{color: '#CDCDCD', fontSize: "14px"}}>(50M)</span> <span className="color-blue"> 上传附件
</span><span style={{color: '#CDCDCD', fontSize: "14px"}}>(50M)</span>
</a> </a>
</div> </div> */}
</div> </div>
</div> </div>
{/* TODOTODO 这里重复的html代码太多如果有其他页面有类似需求需要封装*/} {/* TODOTODO 这里重复的html代码太多如果有其他页面有类似需求需要封装*/}

@ -8,6 +8,9 @@
min-height: 400px; min-height: 400px;
position: relative; position: relative;
} }
#forum_index_list .forum_table .forum_table_item {
background: #fff;
}
.noMemosTip { .noMemosTip {
position: absolute; position: absolute;
right: 10px; right: 10px;
@ -24,7 +27,7 @@
cursor: pointer; cursor: pointer;
} }
#forum_list .return_btn.no_mr { #forum_list .return_btn.no_mr {
margin-right: -16px; margin-right: -24px !important;
} }
div#forum_list>div { div#forum_list>div {
background: #fff; background: #fff;

@ -29,7 +29,7 @@ class PostItem extends Component {
<img alt="用户头像" className="bor-radius-all mt3" height="50" src={getImageUrl(`images/`+memo.image_url)} width="50" /> <img alt="用户头像" className="bor-radius-all mt3" height="50" src={getImageUrl(`images/`+memo.image_url)} width="50" />
</a> </a>
<div className="fl pr" style={{flex: 1}}> <div className="fl pr" style={{flex: 1}}>
<p className="font-16 clearfix" > <p className="font-16 clearfix" style={{ lineHeight: 2 }}>
{/* target="_blank" */} {/* target="_blank" */}
<a href={`/forums/${memo.id}`} target="_blank" title={memo.subject} <a href={`/forums/${memo.id}`} target="_blank" title={memo.subject}
className="clearfix task-hide item_name fl" style={{maxWidth: '750px'}} > className="clearfix task-hide item_name fl" style={{maxWidth: '750px'}} >
@ -46,16 +46,16 @@ class PostItem extends Component {
</p> </p>
<div className="clearfix mt5 color-grey-9"> <div className="clearfix mt5 color-grey-9">
<span className="fl">{memo.username}</span> <span className="fl">{memo.user_name}</span>
{/*todo{memo.username} {/*todo{memo.username}
memo.language && memo.language != 'other' && <span className="fl language-cir-orange mr10 mt3 ml6">{memo.language}</span> memo.language && memo.language != 'other' && <span className="fl language-cir-orange mr10 mt3 ml6">{memo.language}</span>
*/} */}
<span className="fl ml50">{moment(memo.updated_at).fromNow()}</span> {/* <span className="fl ml50">{moment(memo.updated_at).fromNow()}</span> */}
{memo.tag && memo.tag.length ? <span className="fl ml50">来自 {memo.tag.join('/')}</span> : ''} {memo.tag && memo.tag.length ? <span className="fl ml50">来自 {memo.tag.join('/')}</span> : ''}
{/*<span className="fl language-cir-orange mr10 mt3">C++</span>*/} {/*<span className="fl language-cir-orange mr10 mt3">C++</span>*/}
<p className="font-12 fr mr8 color-grey-6"> <p className="font-12 fr mr8 color-grey-6" style={{ marginTop: '4px' }}>
{/* data-tip-down="回复数" <i className="fa fa-comments-o mr5"></i>{memo.replies_count} {/* data-tip-down="回复数" <i className="fa fa-comments-o mr5"></i>{memo.replies_count}
<i className="fa fa-thumbs-o-up mr5"></i>{memo.praise_count}*/} <i className="fa fa-thumbs-o-up mr5"></i>{memo.praise_count}*/}
{memo.replies_count ? {memo.replies_count ?

@ -126,7 +126,7 @@ return function wrap(WrappedComponent) {
} }
const orderTypeMap = { const orderTypeMap = {
'hottest': 'replies_count', 'hottest': 'replies_count',
'newest': 'updated_at', // 'created_at', 'newest': 'updated_at', // 'created_at',
} }
const _search = this.props.history.location.search; const _search = this.props.history.location.search;
const parsed = queryString.parse(_search); const parsed = queryString.parse(_search);
@ -136,7 +136,7 @@ return function wrap(WrappedComponent) {
const params = { const params = {
// replies_count最热 created_at 最新 // replies_count最热 created_at 最新
// s_order: 'replies_count', // s_order: 'replies_count',
page: currentPage - 1, page: currentPage,
// forum: // forum_id // forum: // forum_id
// user_id // user_id
@ -165,7 +165,7 @@ return function wrap(WrappedComponent) {
let paramsUrl = queryString.stringify(params) let paramsUrl = queryString.stringify(params)
const memosUrl = '/api/v1/memos?' + paramsUrl // /${challenge.identifier}/star const memosUrl = '/memos.json?' + paramsUrl // /${challenge.identifier}/star
this.setState({ this.setState({
currentPage, currentPage,
@ -174,7 +174,7 @@ return function wrap(WrappedComponent) {
}) })
// 获取memo list // 获取memo list
axios.get(memosUrl,{ axios.get(memosUrl,{
withCredentials: true, // withCredentials: true,
}) })
.then((response) => { .then((response) => {
const memo_count = response.data.memo_count const memo_count = response.data.memo_count
@ -186,9 +186,9 @@ return function wrap(WrappedComponent) {
this.fetchMemos(maxPage); this.fetchMemos(maxPage);
return; return;
} }
const user = response.data.current_user; // const user = response.data.current_user;
user.tidding_count = response.data.tidding_count; // user.tidding_count = response.data.tidding_count;
this.props.initCommonState(user) // this.props.initCommonState(user)
this.props.initForumState(response.data) this.props.initForumState(response.data)
this.setState({ this.setState({
p_forum_id: params.forum, p_forum_id: params.forum,
@ -242,16 +242,18 @@ return function wrap(WrappedComponent) {
params.forum_id = this.state.p_forum_id; params.forum_id = this.state.p_forum_id;
} }
let paramsUrl = urlStringify(params) let paramsUrl = urlStringify(params)
const set_top_or_down_Url = `/api/v1/memos/${memo.id}/set-top-or-down?${paramsUrl}`; const set_top_or_down_Url = `/memos/${memo.id}/sticky_or_cancel.json?${paramsUrl}`;
// 获取memo list // 获取memo list
axios.get(set_top_or_down_Url, { axios.post(set_top_or_down_Url, {
withCredentials: true, // withCredentials: true,
}) })
.then((response) => { .then((response) => {
const status = response.data.status const status = response.data.status
if (status === 0) { if (status === 0) {
const { memo_list } = response.data; this.fetchMemos(1, '')
this.props.initForumState({ memo_list })
// const { memo_list } = response.data;
// this.props.initForumState({ memo_list })
// 刷新列表 // 刷新列表
// TODO 服务端直接返回第一页列表 // TODO 服务端直接返回第一页列表
// this.props.history.replace('/') // this.props.history.replace('/')
@ -265,16 +267,16 @@ return function wrap(WrappedComponent) {
this.setTop(memo); this.setTop(memo);
} }
onMemoDelete(memo) { onMemoDelete(memo) {
const deleteUrl = `/api/v1/memos/${memo.id}`; const deleteUrl = `/memos/${memo.id}.json`;
// 获取memo list // 获取memo list
axios.delete(deleteUrl, { axios.delete(deleteUrl, {
withCredentials: true, // withCredentials: true,
}) })
.then((response) => { .then((response) => {
const status = response.data.status const status = response.data.status
if (status === 0) { if (status === 0) {
this.props.showSnackbar('删除成功'); this.props.showNotification('删除成功');
// 刷新列表 // 刷新列表
this.fetchMemos(); this.fetchMemos();
} }

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

Loading…
Cancel
Save