master
杨树林 5 years ago
commit 4a05ab8d33

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

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

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

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

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

@ -1029,8 +1029,8 @@ class CoursesController < ApplicationController
normal_status(-1,"课堂暂时没有学生") normal_status(-1,"课堂暂时没有学生")
else else
member_to_xlsx(@course, @all_members, @c_homeworks, @c_exercises, @c_tasks) member_to_xlsx(@course, @all_members, @c_homeworks, @c_exercises, @c_tasks)
filename_ = "#{current_user.real_name}_#{@course.name}_全部成绩" filename_ = "#{current_user.real_name}_#{@course.name}_全部成绩_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
render xlsx: "#{format_sheet_name filename_.strip.first(30)}",template: "courses/export_member_scores_excel.xlsx.axlsx", render xlsx: "#{format_sheet_name filename_.strip}",template: "courses/export_member_scores_excel.xlsx.axlsx",
locals: {course_info:@course_info, activity_level:@user_activity_level, locals: {course_info:@course_info, activity_level:@user_activity_level,
course_scores:@course_user_scores,shixun_works:@shixun_work_arrays, course_scores:@course_user_scores,shixun_works:@shixun_work_arrays,
common_works:@common_work_arrays,group_works:@group_work_arrays,task_works:@task_work_arrays, common_works:@common_work_arrays,group_works:@group_work_arrays,task_works:@task_work_arrays,

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

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

@ -137,7 +137,7 @@ class GraduationTasksController < ApplicationController
format.xlsx{ format.xlsx{
graduation_work_to_xlsx(@work_excel,@task,current_user) graduation_work_to_xlsx(@work_excel,@task,current_user)
task_export_name_ = "#{current_user.real_name}_#{@course.name}_#{@task.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}" task_export_name_ = "#{current_user.real_name}_#{@course.name}_#{@task.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
render xlsx: "#{task_export_name_.strip.first(30)}",template: "graduation_tasks/tasks_list.xlsx.axlsx",locals: {table_columns:@head_cells_column, task_users:@task_cells_column} render xlsx: "#{task_export_name_.strip}",template: "graduation_tasks/tasks_list.xlsx.axlsx",locals: {table_columns:@head_cells_column, task_users:@task_cells_column}
} }
end end
end end

@ -271,7 +271,7 @@ class GraduationTopicsController < ApplicationController
students = course.students.joins(user: :user_extension).order("user_extensions.student_id") students = course.students.joins(user: :user_extension).order("user_extensions.student_id")
graduation_topic_to_xlsx(students,course) graduation_topic_to_xlsx(students,course)
topic_export_name_ = "#{current_user.real_name}_#{course.name}_毕设选题_#{Time.now.strftime('%Y%m%d_%H%M%S')}" topic_export_name_ = "#{current_user.real_name}_#{course.name}_毕设选题_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
render xlsx: "#{topic_export_name_.strip.first(30)}",template: "graduation_topics/export.xlsx.axlsx",locals: {table_columns:@topic_head_cells,topic_users:@topic_body_cells} render xlsx: "#{topic_export_name_.strip}",template: "graduation_topics/export.xlsx.axlsx",locals: {table_columns:@topic_head_cells,topic_users:@topic_body_cells}
rescue Exception => e rescue Exception => e
uid_logger(e.message) uid_logger(e.message)
missing_template missing_template

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

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

@ -950,7 +950,7 @@ class PollsController < ApplicationController
format.xlsx{ format.xlsx{
polls_export_name_ = "#{current_user.real_name}_#{@course.name}_#{@poll.polls_name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}" polls_export_name_ = "#{current_user.real_name}_#{@course.name}_#{@poll.polls_name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
polls_user_commit = poll_commit_result(@poll,@poll_export_questions,@poll_users,@poll_commit_ids) polls_user_commit = poll_commit_result(@poll,@poll_export_questions,@poll_users,@poll_commit_ids)
render xlsx: "#{polls_export_name_.strip.first(30)}",template: "polls/commit_result.xlsx.axlsx",locals: {polls_user_commit:polls_user_commit} render xlsx: "#{polls_export_name_.strip}",template: "polls/commit_result.xlsx.axlsx",locals: {polls_user_commit:polls_user_commit}
} }
end end
end end

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -54,7 +54,8 @@ class User < ApplicationRecord
has_one :onclick_time, :dependent => :destroy has_one :onclick_time, :dependent => :destroy
# 新版私信 # 新版私信
has_many :private_messages, :dependent => :destroy has_many :private_messages, dependent: :destroy
has_many :recent_contacts, through: :private_messages, source: :target
has_many :tidings, :dependent => :destroy has_many :tidings, :dependent => :destroy
has_many :games, :dependent => :destroy has_many :games, :dependent => :destroy

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -53,6 +53,11 @@ Rails.application.routes.draw do
resource :grade_records, only: [:show] resource :grade_records, only: [:show]
resource :watch, only: [:create, :destroy] resource :watch, only: [:create, :destroy]
resources :project_packages, only: [:index] resources :project_packages, only: [:index]
# 私信
resources :private_messages, only: [:index, :create, :destroy]
resources :recent_contacts, only: [:index]
resource :private_message_details, only: [:show]
resource :unread_message_info, only: [:show]
end end
@ -91,6 +96,7 @@ Rails.application.routes.draw do
end end
end end
end end
resources :users_for_private_messages, only: [:index]
resources :myshixuns, param: :identifier, shallow: true do resources :myshixuns, param: :identifier, shallow: true do
member do member do

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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

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

File diff suppressed because one or more lines are too long

@ -420,7 +420,7 @@ table.text-file{}
/*-------------------------------实训路径-------------------------------*/ /*-------------------------------实训路径-------------------------------*/
.path-head{width: 100%;height: 300px;background-image: url("/images/educoder/path.jpg");background-color: #081C4B;background-size: 100% 100%;} .path-head{width: 100%;height: 300px;background-image: url("/images/educoder/path.png");background-color: #081C4B;background-size: 100% 100%;}
.pathNavLine{position: absolute;bottom: -8px;width: 100%;} .pathNavLine{position: absolute;bottom: -8px;width: 100%;}
.path-nav li{float: left;padding: 0px 30px;height: 42px;} .path-nav li{float: left;padding: 0px 30px;height: 42px;}
.path-nav li a{color:#fff;font-size: 16px;display: block; height: 40px;} .path-nav li a{color:#fff;font-size: 16px;display: block; height: 40px;}

@ -41,7 +41,7 @@ export function initAxiosInterceptors(props) {
// proxy = "http://testbdweb.trustie.net" // proxy = "http://testbdweb.trustie.net"
// proxy = "http://testbdweb.educoder.net" // proxy = "http://testbdweb.educoder.net"
// proxy = "https://testeduplus2.educoder.net" // proxy = "https://testeduplus2.educoder.net"
proxy="http://47.96.87.25:48080/" proxy="http://47.96.87.25:48080"
// 在这里使用requestMap控制避免用户通过双击等操作发出重复的请求 // 在这里使用requestMap控制避免用户通过双击等操作发出重复的请求

@ -1,4 +1,6 @@
import { bytesToSize } from 'educoder'; import { bytesToSize, getUrl2 } from 'educoder';
const $ = window.$
export function isImageExtension(fileName) { export function isImageExtension(fileName) {
return fileName ? !!(fileName.match(/.(jpg|jpeg|png|gif)$/i)) : false return fileName ? !!(fileName.match(/.(jpg|jpeg|png|gif)$/i)) : false
} }
@ -36,6 +38,25 @@ export function markdownToHTML(oldContent, selector) {
return content return content
} }
} }
function _doDownload(options) {
$.fileDownload("/api" + options.url, {
successCallback: options.successCallback,
failCallback: options.failCallback
});
}
export function downloadFile(options) {
if ($.fileDownload) {
_doDownload(options)
} else {
const _url_origin = getUrl2()
$.getScript(
`${_url_origin}/javascripts/download/jquery.fileDownload.min.js`,
(data, textStatus, jqxhr) => {
_doDownload(options)
});
}
}
export function appendFileSizeToUploadFile(item) { export function appendFileSizeToUploadFile(item) {
return `${item.title}${uploadNameSizeSeperator}${item.filesize}` return `${item.title}${uploadNameSizeSeperator}${item.filesize}`

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

@ -8,7 +8,7 @@ import { CNotificationHOC } from '../common/CNotificationHOC'
import { RouteHOC } from './common' import { RouteHOC } from './common'
import locale from 'antd/lib/date-picker/locale/zh_CN'; import locale from 'antd/lib/date-picker/locale/zh_CN';
import { WordsBtn, MarkdownToHtml, trigger, queryString } from 'educoder'; import { WordsBtn, MarkdownToHtml, trigger, queryString, downloadFile } from 'educoder';
import axios from 'axios'; import axios from 'axios';
import Modals from '../../modals/Modals'; import Modals from '../../modals/Modals';
import CoursesListType from '../coursesPublic/CoursesListType'; import CoursesListType from '../coursesPublic/CoursesListType';
@ -18,6 +18,8 @@ import '../css/Courses.css'
import CBreadcrumb from '../common/CBreadcrumb' import CBreadcrumb from '../common/CBreadcrumb'
import DownloadMessageysl from "../../modals/DownloadMessageysl"; import DownloadMessageysl from "../../modals/DownloadMessageysl";
import { Spin } from 'antd'
//引入对应跳转的组件 //引入对应跳转的组件
//新建分组/普通作业 //新建分组/普通作业
@ -103,7 +105,7 @@ class CommonWorkDetailIndex extends Component{
/// 确认是否下载 /// 确认是否下载
confirmysl(url,params){ confirmysl(url,params){
axios.get(url,{ axios.get(url+ '&export=true',{
params params
}).then((response) => { }).then((response) => {
if(response.data.status&&response.data.status===-1){ if(response.data.status&&response.data.status===-1){
@ -125,7 +127,21 @@ class CommonWorkDetailIndex extends Component{
} }
}else { }else {
this.props.showNotification(`正在下载中`); this.props.showNotification(`正在下载中`);
window.open("/api"+url, '_blank');
this.setState({ donwloading: true })
downloadFile({
url: url,
params:params,
successCallback: (url) => {
this.setState({ donwloading: false })
console.log('successCallback')
},
failCallback: (responseHtml, url) => {
this.setState({ donwloading: false })
console.log('failCallback')
}
})
// window.open("/api"+url, '_blank');
} }
}).catch((error) => { }).catch((error) => {
console.log(error) console.log(error)
@ -250,7 +266,7 @@ class CommonWorkDetailIndex extends Component{
} }
</div> </div>
{ noTab !== true && <div className="stud-class-set bor-bottom-greyE"> { noTab !== true && <div className="stud-class-set bor-bottom-greyE floatSpinParent">
<div className="mt10 clearfix edu-back-white poll_list pl5"> <div className="mt10 clearfix edu-back-white poll_list pl5">
<Link <Link
onClick={() => this.setState({moduleName: '作品列表'})} onClick={() => this.setState({moduleName: '作品列表'})}
@ -289,18 +305,30 @@ class CommonWorkDetailIndex extends Component{
padding-top: 10px; padding-top: 10px;
padding-bottom: 8px; padding-bottom: 8px;
} }
.floatSpinParent .ant-spin-nested-loading {
float: right;
}
`}</style> `}</style>
{this.props.isAdmin()? <li className="li_line drop_down fr color-blue font-16 mr8 mt20" style={{"padding":"0 20px"}}> {this.props.isAdmin()? <Spin spinning={this.state.donwloading} style={{ }}>
<li className="li_line drop_down fr color-blue font-16 mr8 mt20" style={{"padding":"0 20px"}}>
导出<i className="iconfont icon-xiajiantou font-12 ml2"></i> 导出<i className="iconfont icon-xiajiantou font-12 ml2"></i>
<ul className="drop_down_menu" style={{"right":"-34px","left":"unset","height":"auto"}}> <ul className="drop_down_menu" style={{"right":"-34px","left":"unset","height":"auto"}}>
<li><a href={"javascript:void(0)"} className="color-dark" <li>
<a href={"javascript:void(0)"} className="color-dark"
onClick={() => this.confirmysl(exportResultUrl, exportParams)} onClick={() => this.confirmysl(exportResultUrl, exportParams)}
>导出成绩</a></li> >导出成绩</a>
<li><a href={"javascript:void(0)"} className="color-dark"
</li>
<li>
<a href={"javascript:void(0)"} className="color-dark"
onClick={() => this.confirmysl(exportUrl, exportParams)} onClick={() => this.confirmysl(exportUrl, exportParams)}
>导出作品附件</a></li> >导出作品附件</a>
</li>
</ul> </ul>
</li>:""} </li>
</Spin>:""}
{/* {isAdmin && <a className={"fr color-blue font-16"} href={exportUrl}></a>} {/* {isAdmin && <a className={"fr color-blue font-16"} href={exportUrl}></a>}
{isAdmin && <a className={"fr color-blue font-16"} href={exportResultUrl}>导出成绩</a>} */} {isAdmin && <a className={"fr color-blue font-16"} href={exportResultUrl}>导出成绩</a>} */}

@ -276,7 +276,7 @@ class Testpapersettinghomepage extends Component{
<p className="clearfix mb20 mt10"> <p className="clearfix mb20 mt10">
<a className=" btn colorgrey fl hovercolorblue " onClick={this.goback} >{this.props.coursedata.name}</a> <a className=" btn colorgrey fl hovercolorblue " onClick={this.goback} >{this.props.coursedata.name}</a>
<span className="color-grey-9 fl ml3 mr3">&gt;</span> <span className="color-grey-9 fl ml3 mr3">&gt;</span>
<a className=" btn colorgrey fl hovercolorblue " href={`/courses/${this.props.match.params.coursesId}/exercises/${Commonheadofthetestpaper&&Commonheadofthetestpaper.user_permission.left_banner_id}`} >试卷</a> <Link className=" btn colorgrey fl hovercolorblue " to={`/courses/${this.props.match.params.coursesId}/exercises/${Commonheadofthetestpaper&&Commonheadofthetestpaper.user_permission.left_banner_id}`} >试卷</Link>
<span className="color-grey-9 fl ml3 mr3">&gt;</span> <span className="color-grey-9 fl ml3 mr3">&gt;</span>
<WordsBtn className="fl">试卷详情</WordsBtn> <WordsBtn className="fl">试卷详情</WordsBtn>
</p> </p>

@ -18,7 +18,7 @@ class DownloadMessage extends Component {
setDownload=()=>{ setDownload=()=>{
this.modalCancel(); this.modalCancel();
window.open(`/users/${this.props.user.login}/private_messages`) window.open(`/users/${this.props.user.login}/message_detail?user_id=1`)
} }
modalCancel = () => { modalCancel = () => {
this.setState({ this.setState({

@ -11,7 +11,7 @@ class DownloadMessageysl extends Component {
setDownload=()=>{ setDownload=()=>{
this.props.modalCancel(); this.props.modalCancel();
window.open(`/users/${this.props.user.login}/private_messages`) window.open(`/users/${this.props.user.login}/message_detail?user_id=1`)
} }
render() { render() {

@ -6,29 +6,30 @@ import moment from 'moment';
import '../packageconcnet.css'; import '../packageconcnet.css';
const { Search } = Input; const { Search } = Input;
// let categorylist=[ let categorylist=[
// {name:"全部",value:undefined}, {name:"全部",value:undefined},
// {name:"前端开发",value:"front"}, {name:"前端开发",value:"front"},
// {name:"后端开发",value:"backend"}, {name:"后端开发",value:"backend"},
// {name:"移动开发",value:"mobile"}, {name:"移动开发",value:"mobile"},
// {name:"数据库",value:"database"}, {name:"数据库",value:"database"},
// {name:"云计算和大数据",value:"cloud_compute_and_big_data"}, {name:"云计算和大数据",value:"cloud_compute_and_big_data"},
// {name:"人工智能",value:"ai"}, {name:"人工智能",value:"ai"},
// {name:"其他",value:"other"}, {name:"运维与测试",value:"devops_and_test"},
// ] {name:"其他",value:"other"},
]
// //
// function setcategorylist(val){ function setcategorylist(val){
// let vals="" let vals=""
// categorylist.some((item,key)=> { categorylist.some((item,key)=> {
// if (item.value === val) { if (item.name === val) {
// vals=item.name vals=item.value
// return true return true
// } }
// } }
// ) )
//
// return vals return vals
// } }
@ -157,7 +158,7 @@ class PackageConcent extends Component {
enterButton={<span><Icon type="search" className="mr5"/> 搜索</span>} enterButton={<span><Icon type="search" className="mr5"/> 搜索</span>}
onSearch={ (value)=>this.setdatafuns(value)} /> onSearch={ (value)=>this.setdatafuns(value)} />
<Button type="primary" className="setissues fr" size={"large"}> <Button type="primary" className="setissues fr" size={"large"}>
<a href="/crowdsourcing/new" >发布需求</a> <a href="/crowdsourcings/new" >发布需求</a>
</Button> </Button>
</p> </p>
</p> </p>
@ -214,7 +215,7 @@ class PackageConcent extends Component {
<div className="project-package-item"> <div className="project-package-item">
<div className="item-image"> <div className="item-image">
<img src={"/images/educoder/project_packages/"+item.category+".png"}/> <img src={"/images/educoder/project_packages/"+setcategorylist(item.category_name)+".png"}/>
</div> </div>
<div className=" item-body"> <div className=" item-body">
@ -223,7 +224,7 @@ class PackageConcent extends Component {
<div className=" item-head-title"> <div className=" item-head-title">
<a className={"fl mt3 font-20 font-bd color-dark maxwidth700 "} <a className={"fl mt3 font-20 font-bd color-dark maxwidth700 "}
href={"/crowdsourcing/"+item.id} href={"/crowdsourcings/"+item.id}
title={item.title} title={item.title}
>{item.title}</a> >{item.title}</a>
</div> </div>

@ -2,7 +2,7 @@ import React, {Component} from 'react';
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
import axios from 'axios'; import axios from 'axios';
import { Input ,Icon,Button,Pagination,DatePicker,Breadcrumb} from 'antd'; import { Input ,Icon,Button,Pagination,DatePicker,Breadcrumb} from 'antd';
import { handleDateString,markdownToHTML,bytesToSize} from 'educoder'; import { handleDateString,markdownToHTML,bytesToSize,getImageUrl} from 'educoder';
import NEITaskDetailsModel from './NEITaskDetailsModel'; import NEITaskDetailsModel from './NEITaskDetailsModel';
import moment from 'moment'; import moment from 'moment';
import '../packageconcnet.css'; import '../packageconcnet.css';
@ -229,15 +229,16 @@ class PackageIndexNEITaskDetails extends Component {
} }
goback = () => { goback = () => {
window.history.go(-1) // window.history.go(-1)
window.location.href="/crowdsourcings";
} }
render() { render() {
let {overtype,data}=this.state; let {overtype,data}=this.state;
// console.log(data&&data.creator.login) // console.log(data&&data.creator.login)
// console.log(this.props.current_user.login) console.log(data)
return ( return (
<div> data===undefined?"":<div>
<div className="clearfix"> <div className="clearfix">
<NEITaskDetailsModel <NEITaskDetailsModel
applytype={this.state.applytype} applytype={this.state.applytype}
@ -251,7 +252,7 @@ class PackageIndexNEITaskDetails extends Component {
<Breadcrumb separator={'>'} className={"fl"}> <Breadcrumb separator={'>'} className={"fl"}>
{/*<Breadcrumb.Item>{this.props.current_user.username}</Breadcrumb.Item>*/} {/*<Breadcrumb.Item>{this.props.current_user.username}</Breadcrumb.Item>*/}
<Breadcrumb.Item> <Breadcrumb.Item>
<a href="/crowdsourcing">众包创新</a> <a href="/crowdsourcings">众包创新</a>
</Breadcrumb.Item> </Breadcrumb.Item>
<Breadcrumb.Item><span className={"tabelcli"} title={data&&data.title}>{data&&data.title}</span></Breadcrumb.Item> <Breadcrumb.Item><span className={"tabelcli"} title={data&&data.title}>{data&&data.title}</span></Breadcrumb.Item>
@ -264,7 +265,7 @@ class PackageIndexNEITaskDetails extends Component {
<div className={"stud-class-set coursenavbox edu-back-white mt20"}> <div className={"stud-class-set coursenavbox edu-back-white mt20"}>
<div className={"ant-row contentbox mdInForm "}> <div className={"ant-row contentbox mdInForm "}>
<div className="educontent project-packages-list"> <div className="educontent project-packages-list relative">
{data&&data.status==="pending"?<div> {data&&data.status==="pending"?<div>
<div className="publicpart orangeBlack "></div> <div className="publicpart orangeBlack "></div>
<span className="smalltrangle"></span> <span className="smalltrangle"></span>
@ -280,18 +281,19 @@ class PackageIndexNEITaskDetails extends Component {
<div className="fl edu-back-white "> <div className="fl edu-back-white ">
<a href={`/users/${data&&data.creator.login}`}> <a href={`/users/${data&&data.creator.login}`}>
<img alt="头像" className="radius mt10 ml5" height="70" id="nh_user_logo" name="avatar_image" <img alt="头像" className="radius mt10 ml5" height="70" id="nh_user_logo" name="avatar_image"
src={data&&data.creator.avatar_url} width="70"/> src={`/images/${data&&data.creator.image_url}`}
width="70"/>
</a> </a>
<div className=" edu-back-white pagemancenter mt10 "> <div className=" edu-back-white pagemancenter mt10 ">
{data&&data.creator.name} {data&&data.creator.name}
</div> </div>
{data&&data.creator.login===this.props.current_user.login?"":<div className=" edu-back-white ml5 mt10 " {data&&data.creator.login===this.props.current_user&&this.props.current_user.login?"":<div className=" edu-back-white ml5 mt10 "
onMouseOver={this.setover} onMouseOver={this.setover}
onMouseOut={this.setout} onMouseOut={this.setout}
> >
{overtype===false?<a className="ContacttheTA fl" target="_blank" href={`/users/${this.props.current_user.login}/message_detail?user_id=${data&&data.creator.id}`}> <img alt="头像" class="mr5" src={require('./newsone.png')} />联系TA</a>: {overtype===false?<a className="ContacttheTA fl" target="_blank" href={`/users/${data&&data.creator.login}/message_detail?user_id=${data&&data.creator.id}`}> <img alt="头像" class="mr5" src={require('./newsone.png')} />联系TA</a>:
<a className="ContacttheTAs fl" target="_blank" href={`/users/${this.props.current_user.login}/message_detail?user_id=${data&&data.creator.id}`}> <img alt="头像" className="mr5" <a className="ContacttheTAs fl" target="_blank" href={`/users/${data&&data.creator.login}/message_detail?user_id=${data&&data.creator.id}`}> <img alt="头像" className="mr5"
src={require('./newstwo.png')}/>联系TA</a>} src={require('./newstwo.png')}/>联系TA</a>}
</div>} </div>}
</div> </div>
@ -351,7 +353,7 @@ class PackageIndexNEITaskDetails extends Component {
需求详情 需求详情
{data&&data.status==="pending"&&data&&data.operation.can_select_bidding_user===true?<div className="fr"> {data&&data.status==="pending"&&data&&data.operation.can_select_bidding_user===true?<div className="fr">
<a className="task-btn-nebules fr" href={`/crowdsourcing/${this.props.match.params.id}/edit`}>编辑</a> <a className="task-btn-nebules fr" href={`/crowdsourcings/${this.props.match.params.id}/edit`}>编辑</a>
<a className="task-btn-nebules fr" onClick={this.deletePackages}>删除</a> <a className="task-btn-nebules fr" onClick={this.deletePackages}>删除</a>
</div>:""} </div>:""}
@ -393,18 +395,18 @@ class PackageIndexNEITaskDetails extends Component {
</div> </div>
</div> </div>
<div className={"ysllogin_register_contents ysllogin_register_contentss edu-back-white "} style={{borderTop: '1px solid rgb(234, 234, 234)'}}> <div className={"ysllogin_register_contentss edu-back-white "} style={{borderTop: '1px solid rgb(234, 234, 234)'}}>
<div className="ysllogin_section"> <div className="ysllogin_sections">
<div className="ysldivhome2"> <div className="ysldivhome2s">
<div style={{height: "20px"}}> </div> <div style={{height: "20px"}}> </div>
{data&&data.bidding_users.map((item,key)=>{ {data&&data.bidding_users.map((item,key)=>{
return( return(
<div className="ysldivhomediv1 homehove" key={key}> <div className="ysldivhomediv1s homehove" key={key}>
{item.status==="bidding_won"?<img src={gouxuan} className="yslgouxuanimg"/>:""} {item.status==="bidding_won"?<img src={gouxuan} className="yslgouxuanimg"/>:""}
<a href={`/users/${item.login}`}><img className="div1img" src={item.avatar_url}/></a> <a href={`/users/${item.login}`}><img className="div1imgs" src={getImageUrl("images/"+item.image_url)}/></a>
<div className="textall mt10" title={item.name}> <p className="ptext">{item.name}</p></div> <div className="textall mt10" title={item.name}> <p className="ptext">{item.name}</p></div>
{this.props.current_user.login!=item.login?<a className="ContacttheTAs fl none" target="_blank" href={`/users/${this.props.current_user.login}/message_detail?user_id=${item.id}`}> {this.props.current_user&&this.props.current_user.login!=item.login?<a className="ContacttheTAs fl none" target="_blank" href={`/users/${item.login}/message_detail?user_id=${item.id}`}>
<img alt="头像" className="mr5" src={require('./newstwo.png')}/>联系TA <img alt="头像" className="mr5" src={require('./newstwo.png')}/>联系TA
</a>:""} </a>:""}
</div> </div>
@ -442,15 +444,15 @@ class PackageIndexNEITaskDetails extends Component {
</div> </div>
</div> </div>
</div> </div>
<div className={"ysllogin_register_contents ysllogin_register_contentss edu-back-white "} style={{borderTop: '1px solid rgb(234, 234, 234)'}}> <div className={"ysllogin_register_contentss edu-back-white "} style={{borderTop: '1px solid rgb(234, 234, 234)'}}>
<div className="ysllogin_section"> <div className="ysllogin_sections">
<div className="ysldivhome2"> <div className="ysldivhome2s">
<div style={{height: "20px"}}> </div> <div style={{height: "20px"}}> </div>
{data&&data.bidding_users.map((item,key)=>{ {data&&data.bidding_users.map((item,key)=>{
return( return(
<div className="ysldivhomediv1" onClick={()=>this.Clickteacher2(item.id)} key={key}> <div className="ysldivhomediv1s" onClick={()=>this.Clickteacher2(item.id)} key={key}>
{item.bool===true?<img src={gouxuan} className="yslgouxuanimg"/>:<img src={weigouxuan} className="yslgouxuanimg"/>} {item.bool===true?<img src={gouxuan} className="yslgouxuanimg"/>:<img src={weigouxuan} className="yslgouxuanimg"/>}
<a href={`/users/${item.login}`} target="_blank"><img className="div1img" src={item.avatar_url}/></a> <a href={`/users/${item.login}`} target="_blank"><img className="div1imgs" src={getImageUrl("images/"+item.image_url)}/></a>
<span className={item.bool===true?"textall mt10 color-blue":"textall mt10"} title={item.name}> <p className="ptext">{item.name}</p></span> <span className={item.bool===true?"textall mt10 color-blue":"textall mt10"} title={item.name}> <p className="ptext">{item.name}</p></span>
</div> </div>
) )

@ -1,4 +1,4 @@
.ysldivhome2{ .ysldivhome2s{
display: flex; display: flex;
flex-flow: row wrap; flex-flow: row wrap;
align-content:stretch; align-content:stretch;
@ -6,20 +6,17 @@
margin-bottom: 20px; margin-bottom: 20px;
} }
.ysllogin_register_contents{
display: flex;
margin-top: 20px;
/*justify-content: center;*/
background: #fff;
}
.ysllogin_register_contentss{ .ysllogin_register_contentss{
margin-top:0px !important; margin-top:0px !important;
padding-top: 10px; padding-top: 10px;
padding-bottom: 10px; padding-bottom: 10px;
display: flex;
margin-top: 20px;
/*justify-content: center;*/
background: #fff;
} }
.ysldivhomediv1{ .ysldivhomediv1s{
width: 80px; width: 80px;
height: 130px; height: 130px;
display: flex; display: flex;
@ -33,10 +30,10 @@
margin-left: 64px; margin-left: 64px;
} }
.yslgouxuanimg2{ .yslgouxuanimg2s{
height: 20px; height: 20px;
} }
.div1img{ .div1imgs{
display: flex; display: flex;
justify-content:center; justify-content:center;
width: 80px; width: 80px;
@ -53,7 +50,7 @@
.ptext{ .ptext{
width: 80px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; width: 80px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
} }
.ysllogin_section { .ysllogin_sections {
display: flex; display: flex;
align-items: center; align-items: center;
flex-direction: column; flex-direction: column;

@ -66,13 +66,7 @@ class PackageIndexNEIBannerConcent extends Component {
categories:[] categories:[]
} }
} }
componentDidUpdate = (prevProps) => {
if(prevProps.current_user.username!=this.props.current_user.username){
this.setState({
contact_name:this.props.current_user.username
})
}
}
componentDidMount() { componentDidMount() {
window.document.title = '众包创新' window.document.title = '众包创新'
@ -99,9 +93,7 @@ class PackageIndexNEIBannerConcent extends Component {
}) })
}else{ }else{
this.setState({ console.log(this.props.current_user&&this.props.current_user.username)
contact_name:this.props.current_user.username
})
} }
let Url = `/project_package_categories.json`; let Url = `/project_package_categories.json`;
@ -119,8 +111,24 @@ class PackageIndexNEIBannerConcent extends Component {
console.log(error) console.log(error)
}) })
this.setState({
contact_name:this.props.current_user&&this.props.current_user.username
})
// this.contentMdRef.current.setValue("测试赋值") // this.contentMdRef.current.setValue("测试赋值")
} }
componentDidUpdate = (prevProps) => {
if(prevProps.current_user!=this.props.current_user){
if(this.props.current_user!=undefined){
this.setState({
contact_name:this.props.current_user.username
})
}
}
}
//获取验证码; //获取验证码;
getverificationcode =()=>{ getverificationcode =()=>{
// if (this.state.logins&&this.state.logins.length === 0) { // if (this.state.logins&&this.state.logins.length === 0) {
@ -407,7 +415,7 @@ class PackageIndexNEIBannerConcent extends Component {
// } // }
if(modalCancel===true||this.props.current_user.phone===null){ if(this.props.current_user&&this.props.current_user.phone===null||modalCancel===true){
if(contact_phone===undefined||contact_phone===null||contact_phone===""){ if(contact_phone===undefined||contact_phone===null||contact_phone===""){
this.setState({ this.setState({
contact_phonetype:true contact_phonetype:true
@ -445,7 +453,7 @@ class PackageIndexNEIBannerConcent extends Component {
min_price:parseInt(min_price), min_price:parseInt(min_price),
max_price:parseInt(max_price), max_price:parseInt(max_price),
contact_name: contact_name===null||contact_name===undefined?this.props.current_user.username:contact_name, contact_name: contact_name===null||contact_name===undefined?this.props.current_user.username:contact_name,
contact_phone: contact_phone===undefined?this.props.current_user.phone:contact_phone, contact_phone: contact_phone===undefined?this.props.current_user&&this.props.current_user.phone:contact_phone,
code:code, code:code,
publish:types publish:types
} }
@ -454,7 +462,7 @@ class PackageIndexNEIBannerConcent extends Component {
if(type===true){ if(type===true){
this.props.setPublicationfun(response.data.id) this.props.setPublicationfun(response.data.id)
}else{ }else{
window.location.href="/crowdsourcing/"+response.data.id window.location.href="/crowdsourcings/"+response.data.id
} }
this.setState({ this.setState({
springtype:false springtype:false
@ -493,7 +501,7 @@ class PackageIndexNEIBannerConcent extends Component {
min_price:parseInt(min_price), min_price:parseInt(min_price),
max_price:parseInt(max_price), max_price:parseInt(max_price),
contact_name: contact_name===null||contact_name===undefined?this.props.current_user.username:contact_name, contact_name: contact_name===null||contact_name===undefined?this.props.current_user.username:contact_name,
contact_phone: contact_phone===undefined?this.props.current_user.phone:contact_phone, contact_phone: contact_phone===undefined?this.props.current_user&&this.props.current_user.phone:contact_phone,
code:code, code:code,
publish:types publish:types
} }
@ -502,7 +510,7 @@ class PackageIndexNEIBannerConcent extends Component {
if(type===true){ if(type===true){
this.props.setPublicationfun(response.data.id) this.props.setPublicationfun(response.data.id)
}else{ }else{
window.location.href="/crowdsourcing/"+response.data.id window.location.href="/crowdsourcings/"+response.data.id
} }
this.setState({ this.setState({
springtype:false springtype:false
@ -629,7 +637,7 @@ class PackageIndexNEIBannerConcent extends Component {
// }) // })
// } // }
// } // }
if(modalCancel===true||this.props.current_user.phone===null){ if(this.props.current_user&&this.props.current_user.phone===null||modalCancel===true){
if(e.target.value===undefined||e.target.value===null||e.target.value===""){ if(e.target.value===undefined||e.target.value===null||e.target.value===""){
this.setState({ this.setState({
contact_phonetype:true contact_phonetype:true
@ -707,9 +715,9 @@ class PackageIndexNEIBannerConcent extends Component {
<p className="clearfix" id={"publishtimestart"}> <p className="clearfix" id={"publishtimestart"}>
<div className={"stud-class-set pd30a0 coursenavbox edu-back-white pb20"}> <div className={"stud-class-set pd30a0 coursenavbox edu-back-white pb20"}>
<div className={"ant-row contentbox mdInForm "}> <div className={"ant-row contentbox mdInForm mb20"}>
<div className="ant-form-item-label mb10"> <div className="ant-form-item-label mb10">
<label htmlFor="coursesNew_description" className="ant-form-item-required font-16">请选择需求类型</label> <label htmlFor="coursesNew_description" className="ant-form-item-requireds font-16">请选择需求类型</label>
</div> </div>
<p className="clearfix mb20 shaiContent" > <p className="clearfix mb20 shaiContent" >
@ -724,7 +732,7 @@ class PackageIndexNEIBannerConcent extends Component {
{this.state.categorytypes===true?<div className={"color-red"}>请选择类型</div>:""} {this.state.categorytypes===true?<div className={"color-red"}>请选择类型</div>:""}
<div className="ant-form-item-label mb10"> <div className="ant-form-item-label mb10">
<label htmlFor="coursesNew_description" className="ant-form-item-required font-16" >需求标题和详情</label> <label htmlFor="coursesNew_description" className="ant-form-item-requireds font-16" >需求标题和详情</label>
</div> </div>
<Input placeholder="请输入需求标题示例美食类APP开发最大限制60个字符" maxLength="60" className="input-100-40s mt5 fafafas" <Input placeholder="请输入需求标题示例美食类APP开发最大限制60个字符" maxLength="60" className="input-100-40s mt5 fafafas"
@ -784,7 +792,7 @@ class PackageIndexNEIBannerConcent extends Component {
<div className={"stud-class-set padding30 coursenavbox edu-back-white"} style={{borderTop: '1px solid #EAEAEA'}} id={"publishtime"}> <div className={"stud-class-set padding30 coursenavbox edu-back-white"} style={{borderTop: '1px solid #EAEAEA'}} id={"publishtime"}>
<div className={"ant-row contentbox mdInForm "}> <div className={"ant-row contentbox mdInForm "}>
<div className="ant-form-item-label mb10"> <div className="ant-form-item-label mb10">
<label htmlFor="coursesNew_description" className="ant-form-item-required font-16">工期与预算</label> <label htmlFor="coursesNew_description" className="ant-form-item-requireds font-16">工期与预算</label>
</div> </div>
<p className="clearfix mb20 shaiContent"> <p className="clearfix mb20 shaiContent">
<span className="shaiTitle fl mt5 ml10">竞标截止</span> <span className="shaiTitle fl mt5 ml10">竞标截止</span>
@ -833,35 +841,35 @@ class PackageIndexNEIBannerConcent extends Component {
{this.state.minmaxtype===true?<div className={"color-red ml100"}>最高费用不能小于最低费用</div>:""} {this.state.minmaxtype===true?<div className={"color-red ml100"}>最高费用不能小于最低费用</div>:""}
</p> </p>
<div className="ant-form-item-label mb10"> <div className="ant-form-item-label mb10">
<label htmlFor="coursesNew_description" className="ant-form-item-required font-16" >联系方式</label> <label htmlFor="coursesNew_description" className="ant-form-item-requireds font-16" >联系方式</label>
</div> </div>
<p className="clearfix mb20 shaiContent"> <p className="clearfix mb20 shaiContent">
<span className="shaiTitle fl mt5 ml38">姓名</span> <span className="shaiTitle fl mt5 ml40">姓名</span>
<Input <Input
className={"fafafas"} className={"fafafas"}
style={{"width": "260px"}} style={{"width": "260px"}}
value={this.state.contact_name===null||this.state.contact_name===undefined?this.props.current_user.username:this.state.contact_name} value={this.state.contact_name===null||this.state.contact_name===undefined?this.props.current_user&&this.props.current_user.username:this.state.contact_name}
placeholder="请输入姓名" placeholder="请输入姓名"
onInput={(e)=>this.onChangeContact_name(e)} onInput={(e)=>this.onChangeContact_name(e)}
/> />
{this.state.contact_nametype===true?<div className={"color-red ml100"}>不能为空</div>:""} {this.state.contact_nametype===true?<div className={"color-red ml100"}>不能为空</div>:""}
</p> </p>
{modalCancel===false&&this.props.current_user.phone!=null?<p className="clearfix mb20 shaiContent"> {this.props.current_user&&this.props.current_user.phone!=null&&modalCancel===false?<p className="clearfix mb20 shaiContent">
<span className="shaiTitle fl mt5 ml25">手机号</span> <span className="shaiTitle fl mt5 ml25">手机号</span>
<Input <Input
className={"fafafas fl"} className={"fafafas fl"}
style={{"width": "260px"}} style={{"width": "260px"}}
value={this.state.phones===undefined?this.props.current_user.phone:this.state.phones} value={this.state.phones===undefined?this.props.current_user&&this.props.current_user.phone:this.state.phones}
placeholder="请输入手机号" placeholder="请输入手机号"
disabled={true} disabled={true}
/> />
<a className="fl ml20"> <a className="fl ml20 mt10">
<i className="iconfont icon-bianjidaibeijing font-20 color-blue" onClick={()=>this.editmodels()}></i> <i className="iconfont icon-bianjidaibeijing font-20 color-blue" onClick={()=>this.editmodels()}></i>
</a> </a>
</p>:""} </p>:""}
{/*{this.state.current_userphonetype===true?<div className={"color-red ml100"}>不能为空</div>:""}*/} {/*{this.state.current_userphonetype===true?<div className={"color-red ml100"}>不能为空</div>:""}*/}
{modalCancel===true||this.props.current_user.phone===null?<p className="clearfix mb20 shaiContent"> {this.props.current_user&&this.props.current_user.phone===null||modalCancel===true?<p className="clearfix mb20 shaiContent">
<span className="shaiTitle mt5 fl"> <span className="shaiTitle mt5 fl">
<span className="shaiTitle fl mt5 ml25"> <span className="shaiTitle fl mt5 ml25">
{/*未注册才显示!*/} {/*未注册才显示!*/}
@ -897,7 +905,7 @@ class PackageIndexNEIBannerConcent extends Component {
{/*<Button type="primary" className="defalutSubmitbtn ml10 defalutSubmitbtnmodels">重新发送()</Button>*/} {/*<Button type="primary" className="defalutSubmitbtn ml10 defalutSubmitbtnmodels">重新发送()</Button>*/}
</span> </span>
</span> </span>
<a className="fl mt8"> <a className="fl mt8 mt15">
<span className="font-18 color-blue" onClick={()=>this.modalCancel()}>X</span> <span className="font-18 color-blue" onClick={()=>this.modalCancel()}>X</span>
</a> </a>
</p>:""} </p>:""}

@ -14,10 +14,10 @@ class PackageIndexNEISubmit extends Component {
} }
setageload=(sum)=>{ setageload=(sum)=>{
if(sum===undefined){ if(sum===undefined){
window.location.href="/crowdsourcing/new" window.location.href="/crowdsourcings/new"
}else{ }else{
// this.props.history.push("/project_packages/"+sum) // this.props.history.push("/project_packages/"+sum)
window.location.href="/crowdsourcing/"+sum window.location.href="/crowdsourcings/"+sum
} }
} }
@ -29,8 +29,8 @@ class PackageIndexNEISubmit extends Component {
<p className="clearfix "> <p className="clearfix ">
<div className={"stud-class-set padding200 coursenavbox edu-back-white"}> <div className={"stud-class-set padding200 coursenavbox edu-back-white"}>
<div className={"mb20"}><Icon type="check-circle" theme="filled" className={"fontcircle color-green"}/></div> <div className={"mb20"}><Icon type="check-circle" theme="filled" className={"fontcircle color-green"}/></div>
<div className={"sumbtongs mb5"}>恭喜!</div> <div className={"sumbtongs mb10"}>恭喜!</div>
<div className={"sumbtongs mb5"}>提交成功</div> <div className={"sumbtongs mb10"}>提交成功</div>
<div className={"terraces mb5"}>平台正在审核您的申请审核结果将以平台消息的形式通知您</div> <div className={"terraces mb5"}>平台正在审核您的申请审核结果将以平台消息的形式通知您</div>
<div className="clearfix mt30 mb30 padding251"> <div className="clearfix mt30 mb30 padding251">
<a className="defalutCancelbtns fl" onClick={()=>this.setageload(this.props.id)}>查看发布需求</ a> <a className="defalutCancelbtns fl" onClick={()=>this.setageload(this.props.id)}>查看发布需求</ a>

@ -27,6 +27,12 @@ class PackageIndexNewandEditIndex extends Component{
id:ids id:ids
}) })
} }
goback = () => {
// window.history.go(-1)
window.location.href="/crowdsourcings";
}
render() { render() {
let {setPublication}=this.state; let {setPublication}=this.state;
return ( return (
@ -37,6 +43,7 @@ class PackageIndexNewandEditIndex extends Component{
<p className="clearfix mt20 mb20"> <p className="clearfix mt20 mb20">
<span className="fl font-24 color-grey-3"> <span className="fl font-24 color-grey-3">
{this.props.match.params.id!=undefined?"编辑":"新建"}</span> {this.props.match.params.id!=undefined?"编辑":"新建"}</span>
<a className="color-grey-6 fr font-15 mr20" onClick={this.goback}>返回</a>
</p> </p>
<PackageIndexNEIBanner {...this.props} /> <PackageIndexNEIBanner {...this.props} />

@ -167,7 +167,7 @@
color:#999; color:#999;
} }
.padding251{ .padding251{
padding: 0px 251px; padding: 0px 245px;
} }
.ant-modal-title{ .ant-modal-title{
@ -344,3 +344,17 @@
color: #4CACFF!important color: #4CACFF!important
} }
.ant-form-item-requireds::before {
display: inline-block;
margin-right: 4px;
color: #f5222d;
font-size: 14px;
font-family: SimSun,sans-serif;
line-height: 1;
content: '*';
}
*, *::before, *::after {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}

@ -718,7 +718,7 @@ submittojoinclass=(value)=>{
<li className=""><a href={this.props.Headertop===undefined?"":this.props.Headertop.moop_cases_url}>教学案例</a></li> <li className=""><a href={this.props.Headertop===undefined?"":this.props.Headertop.moop_cases_url}>教学案例</a></li>
<li className=""><a <li className=""><a
// href={this.props.Headertop===undefined?"":this.props.Headertop.crowdsourcing_url} // href={this.props.Headertop===undefined?"":this.props.Headertop.crowdsourcing_url}
href={'/crowdsourcing'} href={'/crowdsourcings'}
>众包创新</a></li> >众包创新</a></li>
<li className={`${activeForums === true ? 'active' : ''}`}><a href={this.props.Headertop===undefined?"":this.props.Headertop.topic_url}>交流问答</a></li> <li className={`${activeForums === true ? 'active' : ''}`}><a href={this.props.Headertop===undefined?"":this.props.Headertop.topic_url}>交流问答</a></li>
<li <li
@ -816,6 +816,7 @@ submittojoinclass=(value)=>{
<li><Link to={`/users/${this.props.current_user===undefined?"":this.props.current_user.login}/shixuns`}>我的实训</Link></li> <li><Link to={`/users/${this.props.current_user===undefined?"":this.props.current_user.login}/shixuns`}>我的实训</Link></li>
<li><Link to={`/users/${this.props.current_user===undefined?"":this.props.current_user.login}/paths`}>我的实践课程</Link></li> <li><Link to={`/users/${this.props.current_user===undefined?"":this.props.current_user.login}/paths`}>我的实践课程</Link></li>
<li><Link to={`/users/${this.props.current_user===undefined?"":this.props.current_user.login}/projects`}>我的项目</Link></li> <li><Link to={`/users/${this.props.current_user===undefined?"":this.props.current_user.login}/projects`}>我的项目</Link></li>
{/*<li><Link to={`/users/${this.props.current_user===undefined?"":this.props.current_user.login}/package`}>我的众包</Link></li>*/}
<li><a href={`/account/profile`}>账号管理</a></li> <li><a href={`/account/profile`}>账号管理</a></li>
{/*<li><a onClick={()=>this.educoderlogin()} >登入测试接口</a></li>*/} {/*<li><a onClick={()=>this.educoderlogin()} >登入测试接口</a></li>*/}
{/*<li><a onClick={()=>this.trialapplications()} >试用申请</a> </li>*/} {/*<li><a onClick={()=>this.trialapplications()} >试用申请</a> </li>*/}

@ -20,7 +20,7 @@ const versionNum = '0001';
// let _url_origin = getUrl() // let _url_origin = getUrl()
let _url_origin=''; let _url_origin='';
if(window.location.port === "3007"){ if(window.location.port === "3007"){
_url_origin="http://47.96.87.25:48080/"; _url_origin="http://47.96.87.25:48080";
} }
// let _url_origin=`https://www.educoder.net`; // let _url_origin=`https://www.educoder.net`;
@ -32,14 +32,14 @@ if (!window['indexHOCLoaded']) {
// $('head').append($('<link rel="stylesheet" type="text/css" />') // $('head').append($('<link rel="stylesheet" type="text/css" />')
// .attr('href', `${_url_origin}/stylesheets/educoder/antd.min.css?1525440977`)); // .attr('href', `${_url_origin}/stylesheets/educoder/antd.min.css?1525440977`));
$('head').append($('<link rel="stylesheet" type="text/css" />') $('head').append($('<link rel="stylesheet" type="text/css" />')
.attr('href', `${_url_origin}/stylesheets/css/edu-common.css?1525440977`)); .attr('href', `${_url_origin}/stylesheets/css/edu-common.css?15254409771`));
$('head').append($('<link rel="stylesheet" type="text/css" />') $('head').append($('<link rel="stylesheet" type="text/css" />')
.attr('href', `${_url_origin}/stylesheets/educoder/edu-main.css?1525440978`)); .attr('href', `${_url_origin}/stylesheets/educoder/edu-main.css?15254409781`));
// index.html有加载 // index.html有加载
$('head').append($('<link rel="stylesheet" type="text/css" />') $('head').append($('<link rel="stylesheet" type="text/css" />')
.attr('href', `${_url_origin}/stylesheets/educoder/edu-all.css?1525440978`)); .attr('href', `${_url_origin}/stylesheets/educoder/edu-all.css?15254409781`));
// $('head').append($('<link rel="stylesheet" type="text/css" />') // $('head').append($('<link rel="stylesheet" type="text/css" />')

@ -719,7 +719,7 @@ class AccountBasic extends Component {
</Form.Item> </Form.Item>
{ {
(!filterDepartments || (filterDepartments && filterDepartments.length==0 ) filterDepartments != undefined && ( (filterDepartments && filterDepartments.length==0 )
|| (departmentsName == '' && !this.state.department_id || (departmentsName == '' && !this.state.department_id
&& (!departments || departments.length == 0) )) && && (!departments || departments.length == 0) )) &&
<div style={{marginLeft: '113px',height:"20px",lineHeight:"20px"}}> <div style={{marginLeft: '113px',height:"20px",lineHeight:"20px"}}>

@ -16,7 +16,7 @@ class ApplyForAddChildOrgModal extends Component{
} }
componentDidUpdate=(prevState)=>{ componentDidUpdate=(prevState)=>{
if(this.props.departmentName && prevState.departmentName != this.props.departmentName){ if(prevState.departmentName != this.props.departmentName){
this.setValue(this.props.departmentName) this.setValue(this.props.departmentName)
} }
} }

@ -17,6 +17,12 @@ import "../../courses/css/Courses.css"
import Trialapplication from '../../login/Trialapplication' import Trialapplication from '../../login/Trialapplication'
const InfosPackage = Loadable({
loader: () => import('./InfosPackage'),
loading:Loading,
})
const InfosCourse = Loadable({ const InfosCourse = Loadable({
loader: () => import('./InfosCourse'), loader: () => import('./InfosCourse'),
loading:Loading, loading:Loading,
@ -378,6 +384,12 @@ class Infos extends Component{
to={`/users/${username}/projects`}>项目</Link> to={`/users/${username}/projects`}>项目</Link>
</li> </li>
{/*<li className={`${moduleName == 'package' ? 'active' : '' }`}>*/}
{/*<Link*/}
{/*onClick={() => this.setState({moduleName: 'package'})}*/}
{/*to={`/users/${username}/package`}>众包</Link>*/}
{/*</li>*/}
{/*{ data && data.identity!="学生" && <li> <a href={`${this.props.Headertop && this.props.Headertop.old_url}/users/${username}?type=m_bank`}>题库</a></li>}*/} {/*{ data && data.identity!="学生" && <li> <a href={`${this.props.Headertop && this.props.Headertop.old_url}/users/${username}?type=m_bank`}>题库</a></li>}*/}
</div> </div>
@ -389,6 +401,15 @@ class Infos extends Component{
{/* --------------------------------------------------------------------- */} {/* --------------------------------------------------------------------- */}
{/* 众包 */}
{/* http://localhost:3007/courses/1309/homework/9300/setting */}
<Route exact path="/users/:username/package"
render={
(props) => (<InfosPackage {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 课堂 */} {/* 课堂 */}
{/* http://localhost:3007/courses/1309/homework/9300/setting */} {/* http://localhost:3007/courses/1309/homework/9300/setting */}
<Route exact path="/users/:username/courses" <Route exact path="/users/:username/courses"
@ -417,6 +438,8 @@ class Infos extends Component{
(props) => (<InfosProject {...this.props} {...props} {...this.state} />) (props) => (<InfosProject {...this.props} {...props} {...this.state} />)
} }
></Route> ></Route>
<Route exact path="/users/:username" <Route exact path="/users/:username"
render={ render={
(props) => (<InfosCourse {...this.props} {...props} {...this.state} />) (props) => (<InfosCourse {...this.props} {...props} {...this.state} />)

@ -0,0 +1,126 @@
import React, { Component } from 'react';
import { SnackbarHOC } from 'educoder';
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import {Tooltip,Menu,Pagination,Spin} from 'antd';
import Loadable from 'react-loadable';
import Loading from '../../../Loading';
import axios from 'axios';
import NoneData from '../../courses/coursesPublic/NoneData'
import {getImageUrl} from 'educoder';
import { TPMIndexHOC } from '../../tpm/TPMIndexHOC';
import { CNotificationHOC } from '../../courses/common/CNotificationHOC'
import "./usersInfo.css"
import Create from './publicCreatNew'
class InfosPackage extends Component{
constructor(props){
super(props);
this.state={
category:undefined,
status:undefined,
page:1,
per_page:16,
totalCount:undefined,
data:undefined,
isSpin:false
}
}
componentDidMount=()=>{
this.setState({
isSpin:true
})
let{category,status,page}=this.state;
this.getCourses(category,status,page);
}
getCourses=(category,status,page)=>{
let url=`/users/${this.props.match.params.username}/courses.json`;
axios.get((url),{params:{
category,
status,
page,
per_page: this.props.is_current && category && page ==1?17:16
}}).then((result)=>{
if(result){
this.setState({
totalCount:result.data.count,
data:result.data,
isSpin:false
})
}
}).catch((error)=>{
console.log(error);
})
}
//切换种类
changeCategory=(cate)=>{
this.setState({
category:cate,
page:1,
isSpin:true
})
let{status}=this.state;
this.getCourses(cate,status,1);
}
//切换状态
changeStatus=(status)=>{
this.setState({
status:status,
page:1,
isSpin:true
})
let{category}=this.state;
this.getCourses(category,status,1);
}
//切换页数
changePage=(page)=>{
this.setState({
page,
isSpin:true
})
let{category,status}=this.state;
this.getCourses(category,status,page);
}
// 进入课堂
turnToCourses=(url,flag)=>{
if(flag){
this.props.history.push(url);
}
}
render(){
let{
category,
status,
page,
data,
totalCount,
isSpin
} = this.state;
let is_current=this.props.is_current;
console.log(this.props.current_user&&this.props.current_user.user_identity==="学生")
return(
<div className="educontent">
<Spin size="large" spinning={isSpin}>
<div className="white-panel edu-back-white pt25 pb25 clearfix ">
<li className={category ? "" : "active"}><a href="javascript:void(0)" onClick={()=>this.changeCategory()}>全部</a></li>
<li className={category=="manage" ? "active" : ""}><a href="javascript:void(0)" onClick={()=>this.changeCategory("manage")}>{is_current ? "我":"TA"}管理的</a></li>
<li className={category=="study" ? "active" : ""}><a href="javascript:void(0)" onClick={()=>this.changeCategory("study")}>{is_current ? "我":"TA"}参与的</a></li>
</div>
<p className="pl25 pr25 clearfix font-12 mb20 mt20">
<span className="fl color-grey-9">{0}</span>
<span className="fr color-grey-9">发布时间</span>
</p>
</Spin>
</div>
)
}
}
export default InfosPackage;

@ -422,7 +422,7 @@ table.text-file{}
/*-------------------------------实训路径-------------------------------*/ /*-------------------------------实训路径-------------------------------*/
.path-head{width: 100%;height: 300px;background-image: url("/images/educoder/path.jpg");background-color: #081C4B;background-size: 100% 100%;} .path-head{width: 100%;height: 300px;background-image: url("/images/educoder/path.png");background-color: #081C4B;background-size: 100% 100%;}
.pathNavLine{position: absolute;bottom: -8px;width: 100%;} .pathNavLine{position: absolute;bottom: -8px;width: 100%;}
.path-nav li{float: left;padding: 0px 30px;height: 42px;} .path-nav li{float: left;padding: 0px 30px;height: 42px;}
.path-nav li a{color:#fff;font-size: 16px;display: block; height: 40px;} .path-nav li a{color:#fff;font-size: 16px;display: block; height: 40px;}

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe ForumsController, type: :controller do
end

@ -0,0 +1,15 @@
require 'rails_helper'
# Specs in this file have access to a helper object that includes
# the ForumsHelper. For example:
#
# describe ForumsHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# expect(helper.concat_strings("this","that")).to eq("this that")
# end
# end
# end
RSpec.describe ForumsHelper, type: :helper do
pending "add some examples to (or delete) #{__FILE__}"
end

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe Forum, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end
Loading…
Cancel
Save