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
def win
package = current_user.project_packages.find(params[:project_package_id])
ProjectPackages::WinBiddingService.call(package, params)
ProjectPackages::WinBiddingService.call(current_package, current_user, params)
render_ok
rescue ProjectPackages::WinBiddingService::Error => ex
render_error(ex.message)

@ -234,6 +234,8 @@ class CoursesController < ApplicationController
def destroy
if @course.is_delete == 0
@course.delete!
Tiding.create!(user_id: @course.tea_id, trigger_user_id: 1, container_id: @course.id,
container_type: 'Course', tiding_type: 'Delete', extra: @course.name)
normal_status(0, "成功")
else
normal_status(-1, "课堂已删除,无需重复操作")
@ -296,7 +298,13 @@ class CoursesController < ApplicationController
@applications = CourseMessage.unhandled_join_course_requests_by_course(@course).
joins("join users on course_messages.course_message_id=users.id").
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
@is_admin = @user_course_identity < Course::PROFESSOR
end
@ -909,6 +917,7 @@ class CoursesController < ApplicationController
CourseAddStudentCreateWorksJob.perform_later(course.id, [current_user.id])
StudentJoinCourseNotifyJob.perform_later(current_user.id, course.id)
end
student_role = 1
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_id: current_user.id, course_message_type: "JoinCourseRequest",
viewed: false)
course_message.content = 2 if params[:professor].present? && params[:professor].to_i == 1
course_message.content = 3 if params[:assistant_professor].present? && params[:assistant_professor].to_i == 1
if params[:professor].present? && params[:professor].to_i == 1
course_message.content = 2
role = 9
message = "教师申请已提交,请等待审核"
else
course_message.content = 3
role = 7
message = "助教申请已提交,请等待审核"
end
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)
message = "#{course_message.content == 2 ? '助教' : '教师'}申请已提交,请等待审核"
# message = "#{course_message.content == 2 ? '教师' : '助教'}申请已提交,请等待审核"
else
message = "#{existing_course_message.content == 2 ? '助教' : '教师'}申请已提交,请等待审核"
message = "#{existing_course_message.content == '2' ? '教师' : '助教'}申请已提交,请等待审核"
end
else
message = "您已是课堂成员"
end
teacher_role = 1
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}
elsif current_user.student_of_course?(course)
elsif student_role
render json: { status: 0, message: "加入成功", course_id: course.id}
else
normal_status(message)

@ -1,7 +1,7 @@
class DiscussesController < ApplicationController
LIMIT = 10
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
page = params[:page].to_i

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

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

@ -22,7 +22,7 @@ class LibrariesController < ApplicationController
end
@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)
@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 :validate_memo_params, only: [:create, :update]
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
# GET /memos
@ -85,7 +85,7 @@ class MemosController < ApplicationController
params[:tags].each do |tag|
MemoTagRepertoire.create!(memo_id: @memo.id, tag_repertoire_id: tag)
end
normal_status("帖子创建成功")
render :json => {memo_id: @memo.id, status: 0, message: "帖子创建成功"}
rescue Exception => e
tip_exception("帖子创建失败,原因:#{e}")
raise ActiveRecord::Rollback
@ -132,7 +132,7 @@ class MemosController < ApplicationController
def hidden
tip_exception("不能对主贴进行隐藏操作") if @memo.parent_id.nil?
begin
@memo.update_attributes!(hidden: !@memo.hidden)
@memo.update_attributes!(hidden: @memo.hidden == 0 ? 1 : 0)
normal_status("更新成功")
rescue Exception => e
tip_exception("更新失败,原因:#{e}")
@ -147,16 +147,15 @@ class MemosController < ApplicationController
ActiveRecord::Base.transaction do
begin
memo = Memo.find_by!(id: params[:parent_id])
reply = Memo.new
reply.content = params[:content]
reply.author = current_user
reply.forum_id = memo.forum_id
reply.subject = memo.subject
reply.root_id = memo.root_id || memo.id
memo.children << reply
m = Memo.find_by!(id: reply.root_id)
@reply = Memo.new
@reply.content = params[:content]
@reply.author = current_user
@reply.forum_id = memo.forum_id
@reply.subject = memo.subject
@reply.root_id = memo.root_id || memo.id
memo.children << @reply
m = Memo.find_by!(id: @reply.root_id)
m.increment!(:all_replies_count)
normal_status("回复成功")
rescue Exception => e
tip_exception("回复失败,原因:#{e}")
raise ActiveRecord::Rollback
@ -183,10 +182,6 @@ class MemosController < ApplicationController
tip_exception(403, "无权限操作") unless @memo.author == current_user || current_user.admin? || current_user.business?
end
def is_admin
tip_exception(403, "无权限操作") unless current_user.admin? || current_user.business?
end
# Never trust parameters from the scary internet, only allow the white list through.
def memo_params
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[:content].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

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

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

@ -1,7 +1,7 @@
class ProjectPackagesController < ApplicationController
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?

@ -164,7 +164,7 @@ class ShixunsController < ApplicationController
@new_shixun = Shixun.new
@new_shixun.attributes = @shixun.attributes.dup.except("id","user_id","visits","gpid","status", "identifier", "averge_star",
"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.averge_star = 5
@new_shixun.identifier = generate_identifier Shixun, 8
@ -406,9 +406,10 @@ class ShixunsController < ApplicationController
end
end
@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_schools.delete_all
logger.info("##########scope_partment:###{params[:scope_partment]}")
# scope_partment: 高校的名称
if params[:scope_partment].present?
arr = []

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

@ -9,6 +9,8 @@ class Users::PrivateMessagesController < Users::BaseController
query = "SELECT subquery.*, COUNT(*) message_count FROM (#{subquery}) subquery "\
"GROUP BY subquery.target_id ORDER BY subquery.send_time desc LIMIT #{limit_value} OFFSET #{offset_value}"
@messages = PrivateMessage.select('*').from("(#{query}) AS query").includes(target: :user_extension)
observed_user.private_messages.only_unread.update_all(status: 1)
end
def create

@ -1,5 +1,5 @@
module CourseDecorator
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

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

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

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

@ -194,7 +194,7 @@ module CoursesHelper
# 获取课堂的资源数
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
# 获取课堂的作业数

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

@ -3,8 +3,18 @@
class ApplyUserAuthentication < ApplicationRecord
belongs_to :user
has_many :tidings, :as => :container, :dependent => :destroy
scope :real_name_auth, -> { where(auth_type: 1) }
scope :professional_auth, -> { where(auth_type: 2) }
scope :processing, -> { where(status: 0) }
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

@ -11,7 +11,7 @@ class CourseMessage < ApplicationRecord
def pass!
update!(status: :PASSED)
send_deal_tiding
send_deal_tiding(1)
end
def application_user
@ -20,16 +20,16 @@ class CourseMessage < ApplicationRecord
def reject!
update!(status: :REJECTED)
send_deal_tiding
send_deal_tiding(2)
end
private
def send_deal_tiding
def send_deal_tiding deal_status
# 发送申请处理结果消息
Tiding.create!(
user_id: user_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
user_id: course_message_id, trigger_user: User.current, container_id: course_id, container_type: 'DealCourse',
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)

@ -11,6 +11,7 @@ class JournalsForMessage < ApplicationRecord
scope :parent_comment, -> { where(m_parent_id: nil)}
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_id", # 留言所属类型的id
@ -25,6 +26,8 @@ class JournalsForMessage < ApplicationRecord
# "is_comprehensive_evaluation", # 1 教师评论、2 匿评、3 留言
# "hidden", 隐藏
after_create :send_tiding
# 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")
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

@ -10,6 +10,8 @@ class Library < ApplicationRecord
has_many :attachments, as: :container
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

@ -38,6 +38,7 @@ class Shixun < ApplicationRecord
belongs_to :user
# 实训服务配置
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 ? ",
"%#{keyword}%", "%#{keyword}%") }
@ -62,6 +63,8 @@ class Shixun < ApplicationRecord
scope :field_for_recommend, lambda{ select([:id, :name, :identifier, :myshixuns_count]) }
scope :find_by_ids,lambda{|k| where(id:k)}
after_create :send_tiding
# REDO: 
def propaedeutics
shixun_info.try(:propaedeutics)
@ -242,4 +245,11 @@ class Shixun < ApplicationRecord
def finished_challenges_count(user)
Game.joins(:myshixun).where(user_id: user.id, status: 2, myshixuns: { shixun_id: id }).count
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

@ -17,7 +17,12 @@ class StudentGraduationTopic < ApplicationRecord
scope :is_refused, -> {where(status: 2)}
scope :is_accepted, -> {where(status: 1)}
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

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

@ -234,7 +234,7 @@ class User < ApplicationRecord
# 课堂的老师(创建者、老师、助教),不用考虑当前身份
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
# 是否是教师,课堂管理员或者超级管理员

@ -54,7 +54,7 @@ class ProjectPackages::SaveService < ApplicationService
raise Error, '验证码不能为空' if params[:code].blank?
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
def deal_attachments

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

@ -1 +1,2 @@
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.creator course.teacher.real_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.tasks_count get_tasks_count course
json.visits course.visits

@ -11,6 +11,6 @@ if message.m_parent_id
json.can_delete message.can_delete(identity)
else
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
end

@ -5,12 +5,17 @@ json.libraries do
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.published_at library.display_published_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

@ -2,7 +2,7 @@ library = current_library
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.created_at library.display_created_at
@ -10,6 +10,7 @@ json.created_at library.display_created_at
# 创建者
json.creator do
json.partial! 'users/user_simple', user: library.user
json.school_name library.user.school_name
end
# 封面
@ -37,7 +38,7 @@ json.operation do
json.can_deletable 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
json.can_deletable false
json.can_editable false

@ -9,6 +9,6 @@ json.memo do
json.tag memo.tag_repertoires.map(&:name)
json.time memo.created_at
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
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.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.new_tiding tiding.unread?(@onclick_time)

@ -3,6 +3,7 @@ json.message 'success'
json.private_message do
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.sender do
json.partial! 'users/user_simple', user: @message.sender

@ -16,5 +16,11 @@ json.project_packages do
json.deadline_at package.display_deadline_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

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

@ -5,6 +5,7 @@ Rails.application.routes.draw do
mount Sidekiq::Web => '/sidekiq', :constraints => AdminConstraint.new
get 'attachments/download/:id', to: 'attachments#show'
get 'attachments/download/:id/:filename', to: 'attachments#show'
resources :edu_settings
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-loadable": "^5.3.1",
"react-monaco-editor": "^0.25.1",
"react-player": "^1.11.1",
"react-redux": "5.0.7",
"react-router": "^4.2.0",
"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_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-part:hover{background-color: #F5F5F5;}
.private-part.active{background-color: #F5F5F5;}

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

@ -54,4 +54,5 @@ html, body {
/* md多空格 */
.markdown-body p {
white-space: pre-wrap;
margin-bottom: 0px !important;
}

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

@ -43,7 +43,6 @@ export function initAxiosInterceptors(props) {
// proxy = "https://testeduplus2.educoder.net"
proxy="http://47.96.87.25:48080"
// 在这里使用requestMap控制避免用户通过双击等操作发出重复的请求
// 如果需要支持重复的请求考虑config里面自定义一个allowRepeat参考来控制
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 { getUrl2 } from "educoder";
import ReactPlayer from 'react-player'
const $ = window.$
let _url_origin = getUrl2()
@ -10,33 +11,54 @@ class Clappr extends Component{
this.state={
}
}
componentWillUnmount() {
this['player'+this.props.id] && this['player'+this.props.id].destroy()
}
componentDidMount() {
return;
const source = this.props.source || "http://your.video/here.mp4"
const { id } = this.props
const { id, type } = this.props
const _id = `#_player${id}`
if (!window['Clappr'] && window['ClapprLoading'] == true) {
setTimeout(() => {
this.componentDidMount()
}, 300)
return;
}
// && window['clappr-playback-rate-plugin']
if (window['Clappr'] ) {
const player = new window.Clappr.Player({
// 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,
plugins: {
'core': [window.Clappr.MediaControl, window.Clappr.Playback]
}
height: type == 'mp3' ? 60 : 360,
hideMediaControl: type == 'mp3' ? false : true,
// plugins: {
// 'core': [window.Clappr.MediaControl, window['clappr-playback-rate-plugin'].default]
// }
});
} else {
window['ClapprLoading'] = true;
$.getScript(
`${_url_origin}/javascripts/media/clappr.min.js`,
(data, textStatus, jqxhr) => {
window.clappr = window.Clappr
$.getScript(
`${_url_origin}/javascripts/media/clappr-playback-rate-plugin.min.js`,
(data, textStatus, jqxhr) => {
const player = new window.Clappr.Player({
// $.getScript(
// `${_url_origin}/javascripts/media/clappr-playback-rate-plugin.min.js`,
// (data, textStatus, jqxhr) => {
this['player'+id] = new window.Clappr.Player({
source: source, parentId: _id,
plugins: {
'core': [window.Clappr.MediaControl, window['clappr-playback-rate-plugin'].default]
}
height: type == 'mp3' ? 60 : 360,
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(){
let { source, id, className } = this.props;
let { source, id, className, type } = this.props;
const _id = `_player${id}`
return(
<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 {
margin-right: 16px;
}
`}</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>
)
}

@ -3,7 +3,8 @@ import { from } from '_array-flatten@2.1.2@array-flatten';
// export { default as OrderStateUtil } from '../routes/Order/components/OrderStateUtil';
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 { SnackbarHOC as SnackbarHOC } from './SnackbarHOC';

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

@ -1,4 +1,6 @@
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Redirect } from 'react-router';
import PropTypes from 'prop-types';
@ -29,7 +31,11 @@ class EvaluateSuccessEffectDisplay extends Component {
}
}
hidepicture = () => {
window.$('#picture_display').hide();
const dom = document.getElementById('picture_display');
ReactDOM.unmountComponentAtNode(dom)
// window.$('#picture_display').hide();
window.$('.data-tip-right').hide()
}
renderContent = () => {
// qrcode
@ -107,11 +113,11 @@ class EvaluateSuccessEffectDisplay extends Component {
<EffectDisplayContent
typeName="音频"
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
? <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
? <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>
)
} else if (type == 'mp4') {
@ -119,11 +125,11 @@ class EvaluateSuccessEffectDisplay extends Component {
<EffectDisplayContent
typeName="视频"
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
? <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
? <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>
)
}

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

@ -301,7 +301,7 @@ class BoardsNew extends Component{
`}</style>
<div className="edu-class-container edu-position courseForm">
<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 },
{ name: this.isEdit ? '帖子编辑' : '帖子新建'}
]}></CBreadcrumb>
@ -416,7 +416,7 @@ class BoardsNew extends Component{
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>
)}
</Form.Item>

@ -546,7 +546,7 @@ class TopicDetail extends Component {
}
`}</style>
<CBreadcrumb className={'independent'} items={[
{ to: current_user.first_category_url, name: this.props.coursedata.name},
{ to: current_user&&current_user.first_category_url, name: this.props.coursedata.name},
{ to: `/courses/${courseId}/boards/${boardId}`, name: memo.board_name },
{ name: '帖子详情'}
]}></CBreadcrumb>

@ -440,7 +440,7 @@ class Boards extends Component{
</div>
</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">

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

@ -722,7 +722,7 @@ class CommonWorkSetting extends Component{
late_time: late_time ? new Date(late_time) : late_time, // 补交截止时间
anonymous_comment: anonymous_comment, // true: 启用匿评 false:未启用匿评
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, // 匿评数
absence_penalty: absence_penalty, // 匿评扣分
anonymous_appeal: anonymous_appeal, // true: 启用匿评申诉, false:未启用

@ -446,7 +446,7 @@ class NewWork extends Component{
}],
})(
<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> }
<Upload {...uploadProps} className="upload_1 newWorkUpload">
@ -508,7 +508,7 @@ class NewWork extends Component{
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>
)}
<Upload {...answerUploadProps} className="upload_1">

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

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

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

@ -408,7 +408,7 @@ class ExerciceNew extends Component{
`}</style>
<div className="edu-class-container edu-position courseForm">
{ current_user && <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}/exercises/${left_banner_id}`, name: '试卷列表' },
{ name: this.isEdit ? '编辑试卷' : '新建试卷'}
]}></CBreadcrumb> }

@ -171,7 +171,8 @@ class Testpapersettinghomepage extends Component{
}
console.log("170");
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){
return
}
@ -195,13 +196,13 @@ class Testpapersettinghomepage extends Component{
}else {
this.setState({ donwloading: true })
downloadFile({
url: url+`?${queryString.stringify(params)}`,
url: urll,
successCallback: (url) => {
this.setState({ donwloading: false });
this.setState({ donwloading: false })
console.log('successCallback')
},
failCallback: (responseHtml, url) => {
this.setState({ donwloading: false });
this.setState({ donwloading: false })
console.log('failCallback')
}
})

@ -187,7 +187,7 @@ class SingleEditor extends Component{
<span className="color-grey-9 font-12 fl">客观题由系统自动评分请设置标准答案</span>
</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})}
ref="titleEditor"
></TPMMDEditor>

@ -152,14 +152,14 @@ class MainEditor extends Component{
<span className="color-grey-9 font-12 fl">主观题未作答的情况下自动评为零分</span>
</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})}
noStorage={true} ref="titleEditor"
></TPMMDEditor>
<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]})}
noStorage={true}
></TPMMDEditor>

@ -57,7 +57,7 @@ class NullChildEditor extends Component{
className={'nullChildEditor'}
placeholder={`请输入参考答案${itemIndex == 0 ?'':'(可选)'}`}
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)}
></DMDEditor>
</div>

@ -297,7 +297,7 @@ class NullEditor extends Component{
<span className="color-grey-9 font-12 fl">客观题由系统自动评分允许手动调分请设置标准答案 支持最多5个空每空得分按照本题的总分平均计算</span>
</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})}
onPlaceholderChange={this.onPlaceholderChange} showNullButton={exerciseIsPublish ? false : true}
ref="titleEditor"

@ -250,7 +250,7 @@ class ShixunEditor extends Component{
style={{ marginBottom: '10px'}}
></Input>
{/* <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})}
noStorage={true}
></TPMMDEditor>

@ -242,7 +242,7 @@ class SingleEditor extends Component{
<span className="color-grey-9 font-12 fl">客观题由系统自动评分请设置标准答案</span>
</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})}
ref="titleEditor"
@ -265,7 +265,7 @@ class SingleEditor extends Component{
<DMDEditor
ref={`optionEditor${index}`}
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)}
initValue={item}
></DMDEditor>

@ -767,7 +767,7 @@ class GraduationTasks extends Component{
<div className="alltask edu-back-white"
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="educontent mt10 mb50">
<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>
<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>

@ -323,7 +323,7 @@ class GraduateTopicNew extends Component{
`}</style>
<div className="edu-class-container edu-position courseForm">
<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>
<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>

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

@ -195,7 +195,7 @@ class AddStudentModal extends Component{
<div className="df">
<span className="mr10">姓名:</span>
<Input allowClear placeholder="请输入真实姓名" value={name} onChange={(e) => {this.setState({name: e.target.value})}}
style={{ width: '242px'}}
style={{ width: '221px'}}
></Input>
<span className="label" style={{ minWidth: '36px' }}>单位:</span>
{/* <Input allowClear placeholder="" value={school_name} onChange={(e) => {this.setState({school_name: e.target.value})}}
@ -210,14 +210,7 @@ class AddStudentModal extends Component{
>搜索</a>
</div>
{/* <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' }}>
<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>
<span className="fl with25">
<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>
</span>
<span className="fl with25">
@ -271,6 +269,14 @@ class AddStudentModal extends Component{
</InfiniteScroll>
</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> }
</Spin>
</ModalWrapper>

@ -677,9 +677,9 @@ class studentsList extends Component{
</div>
<Spin size="large" spinning={this.state.isSpin}>
<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>
</Checkbox.Group>
</Checkbox.Group> }
</div>
</Spin>
</div>

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

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

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

@ -246,7 +246,7 @@ class TraineetraininginformationModal extends Component {
{this.props.boolgalist === false?
<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 ">
<style>
{
@ -296,7 +296,7 @@ class TraineetraininginformationModal extends Component {
<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 ">
<style>
{

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

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

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

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

@ -41,9 +41,9 @@ class MemoDetailMDEditortwo extends Component {
window.__tt = 400;
setTimeout(() => {
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) => {
this.setState({
@ -59,7 +59,7 @@ class MemoDetailMDEditortwo extends Component {
window.commentMDEditor = commentMDEditor;
}, window.__tt)
}
};
componentDidMount() {
!this.props.usingMockInput && this.initMDEditor()
}

@ -7,16 +7,17 @@ import PropTypes from 'prop-types';
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
import Upload from 'rc-upload';
// import Upload from 'rc-upload';
import axios from 'axios'
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 $ = window.$;
@ -130,6 +131,7 @@ const languageSeparator = '/'
class MemoNew extends Component {
constructor(props) {
super(props)
this.mdRef = React.createRef();
// https://testbdweb.trustie.net/uploads.js?attachment_id=1&filename=jqui.js
// https://ant.design/components/upload-cn/
@ -167,32 +169,34 @@ class MemoNew extends Component {
repertoires: [],
currentSelectRepertoiresIndex: -1,
repertoiresTagMap: {}
repertoiresTagMap: {},
fileList: []
}
}
onCommit() {
const { memoSubject, memoRepertoire, memoLanguage, currentMemoId, memoType } = this.state;
const { showSnackbar } = this.props;
const { showNotification } = this.props;
if (!memoSubject) {
showSnackbar('请先输入话题名称')
showNotification('请先输入话题名称')
return
}
let mdVal;
try {
mdVal = this.taskpass_editormd.getValue()
mdVal = this.mdRef.current.getValue()
} catch (e) {
showSnackbar('编辑器还未加载完毕,请稍后')
showNotification('编辑器还未加载完毕,请稍后')
return
}
if (!mdVal) {
showSnackbar('请先输入话题内容')
showNotification('请先输入话题内容')
return
}
// !memoRepertoire ||
if (memoType === 5 && ( !memoLanguage || memoLanguage.length === 0 )) {
showSnackbar('请先选择技术标签')
showNotification('请先选择技术标签')
return
}
/*
@ -205,23 +209,27 @@ class MemoNew extends Component {
// collect attachments
const $ = window.$;
const attachmentsMap = {};
$('#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
}
const attachmentIds = this.state.fileList.map(item => {
return item.response ? item.response.id : item.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) {
this.updateMemo(attachmentsMap)
this.updateMemo(attachmentIds)
} else {
this.newMemo(attachmentsMap)
this.newMemo(attachmentIds)
}
}
onCancel() {
@ -235,23 +243,23 @@ class MemoNew extends Component {
}
updateMemo(attachmentsMap) {
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);
const newMemoUrl = `/api/v1/memos/${currentMemoId}/update`
axios.post(newMemoUrl, {
const newMemoUrl = `/memos/${currentMemoId}.json`
axios.put(newMemoUrl, {
content_changed: this.contentChanged,
tags: memoLanguage,
memo:{
// memo:{
subject: memoSubject ,
content: mdVal,
forum_id: memoType,
repertoire_name: memoRepertoire,
// language: memoLanguage.join(languageSeparator),
//
},
attachments: attachmentsMap
// },
attachment_ids: attachmentsMap
}, {
withCredentials: true,
// withCredentials: true,
})
.then((response) => {
const { status, message, memo_id } = response.data;
@ -259,7 +267,7 @@ class MemoNew extends Component {
window.$("html,body").animate({"scrollTop":0})
this.props.history.push(`/forums/${currentMemoId}`)
} else {
this.props.showSnackbar(message)
this.props.showNotification(message)
}
}).catch((error) => {
console.log(error)
@ -267,21 +275,21 @@ class MemoNew extends Component {
}
newMemo(attachmentsMap) {
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, {
tags: memoLanguage,
memo:{
// memo:{
subject: memoSubject ,
content: mdVal,
forum_id: memoType,
repertoire_name: memoRepertoire,
// repertoire_name: memoRepertoire,
},
attachments: attachmentsMap
// },
attachment_ids: attachmentsMap
}, {
withCredentials: true,
// withCredentials: true,
})
.then((response) => {
const { status, message, memo_id } = response.data;
@ -289,16 +297,16 @@ class MemoNew extends Component {
window.$("html,body").animate({"scrollTop":0})
this.props.history.push(`/forums/${memo_id}`)
} else {
this.props.showSnackbar(message)
this.props.showNotification(message)
}
}).catch((error) => {
console.log(error)
})
}
componentDidMount() {
const newMemoUrl = `/api/v1/memos/new`
const newMemoUrl = `/memos/new.json`
axios.get(newMemoUrl,{
withCredentials: true,
// withCredentials: true,
})
.then((response) => {
const data = response.data;
@ -319,9 +327,9 @@ class MemoNew extends Component {
// repertoiresTagMap
})
const user = response.data.current_user;
user.tidding_count = response.data.tidding_count;
this.props.initCommonState(user)
// const user = response.data.current_user;
// user.tidding_count = response.data.tidding_count;
// this.props.initCommonState(user)
// 初始化 csrf meta
const $ = window.$
@ -336,26 +344,39 @@ class MemoNew extends Component {
const { match } = this.props
const memoId = match.params.memoId;
if (memoId) {
const memoUrl = `/api/v1/memos/${match.params.memoId}/edit`;
const memoUrl = `/memos/${match.params.memoId}/edit.json`;
axios.get(memoUrl,{
withCredentials: true,
// withCredentials: true,
})
.then((response) => {
const tag_list = response.data.tag_list
if (tag_list) {
// this.setState({...response.data})
const { content, forum_id, id, tag, repertoire_name, subject,
current_user, tag_list, attachments_url } = response.data;
const { content, forum_id, id, repertoire_name, subject,
current_user, tag_list, attachments_url, memo_tags, attachments } = response.data;
this.initMD(content);
// this.onRepertoiresChange(repertoire_name)
// tag -> memo_tags
const tag = memo_tags;
let memoLanguage = []
if (tag) {
memoLanguage = tag.map((item, index) => {
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({
currentMemoId: id,
fileList,
currentMemoId: memoId,
memoSubject: subject,
memoType: forum_id,
memoRepertoire: repertoire_name,
@ -377,7 +398,7 @@ class MemoNew extends Component {
window.$("html,body").animate({"scrollTop":0})
this.props.initForumState({
current_user,
// current_user,
tag_list
})
}
@ -394,6 +415,8 @@ class MemoNew extends Component {
}
initMD(initValue) {
return;
this.contentChanged = false;
const placeholder = "";
// amp;
@ -458,7 +481,7 @@ class MemoNew extends Component {
onTagChange(value) {
if (value && value.length > 3) {
this.props.showSnackbar(`最多选择3个技术标签`)
this.props.showNotification(`最多选择3个技术标签`)
return;
}
@ -523,6 +546,55 @@ class MemoNew extends Component {
)
})
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() {
const { match, history } = this.props
@ -530,8 +602,28 @@ class MemoNew extends Component {
// repertoires, repertoiresTagMap, currentSelectRepertoiresIndex, memoRepertoire,
tag_list,
memoSubject, memoType,
memoLanguage, attachments_url } = this.state;
memoLanguage, attachments_url, fileList } = this.state;
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 (
<div >
<div className="pt20 pl20 pr20 pb20 bor-bottom-greyE clearfix" style={{background: '#fff'}}>
@ -566,9 +658,12 @@ class MemoNew extends Component {
<div className="df">
<span className="mr30 color-orange pt10">*</span>
<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>
</div>
</div> */}
<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>
</div>
@ -581,7 +676,7 @@ class MemoNew extends Component {
<form className="newForm">
{/* <form className="newForm">
<span id={`attachments_fields`} className="attachments_fields"
xmlns="http://www.w3.org/1999/html">
{ attachments_url && !!attachments_url.length &&
@ -600,18 +695,38 @@ class MemoNew extends Component {
style={{'display':'none'}} type="file">
</input>
</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 {...uploadProps} className="upload_1 memo_upload" >
<Button className="uploadBtn">
<Icon type="upload" /> 上传附件
</Button>
(单个文件150M以内)
</Upload>
{/* 请求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()}
data-tip-down="请选择文件上传">
<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>
</div>
</div> */}
</div>
</div>
{/* TODOTODO 这里重复的html代码太多如果有其他页面有类似需求需要封装*/}

@ -7,6 +7,9 @@
#forum_index_list {
min-height: 400px;
position: relative;
}
#forum_index_list .forum_table .forum_table_item {
background: #fff;
}
.noMemosTip {
position: absolute;
@ -24,7 +27,7 @@
cursor: pointer;
}
#forum_list .return_btn.no_mr {
margin-right: -16px;
margin-right: -24px !important;
}
div#forum_list>div {
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" />
</a>
<div className="fl pr" style={{flex: 1}}>
<p className="font-16 clearfix" >
<p className="font-16 clearfix" style={{ lineHeight: 2 }}>
{/* target="_blank" */}
<a href={`/forums/${memo.id}`} target="_blank" title={memo.subject}
className="clearfix task-hide item_name fl" style={{maxWidth: '750px'}} >
@ -46,16 +46,16 @@ class PostItem extends Component {
</p>
<div className="clearfix mt5 color-grey-9">
<span className="fl">{memo.username}</span>
<span className="fl">{memo.user_name}</span>
{/*todo{memo.username}
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> : ''}
{/*<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}
<i className="fa fa-thumbs-o-up mr5"></i>{memo.praise_count}*/}
{memo.replies_count ?

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

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

Loading…
Cancel
Save