diff --git a/app/assets/javascripts/forums.js b/app/assets/javascripts/forums.js new file mode 100644 index 000000000..dee720fac --- /dev/null +++ b/app/assets/javascripts/forums.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. diff --git a/app/assets/javascripts/memos.js b/app/assets/javascripts/memos.js new file mode 100644 index 000000000..dee720fac --- /dev/null +++ b/app/assets/javascripts/memos.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. diff --git a/app/assets/stylesheets/forums.scss b/app/assets/stylesheets/forums.scss new file mode 100644 index 000000000..fafd631e1 --- /dev/null +++ b/app/assets/stylesheets/forums.scss @@ -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/ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index f01ad4b6b..c76ca51a7 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -38,6 +38,7 @@ class ApplicationController < ActionController::Base def user_course_identity @user_course_identity = current_user.course_identity(@course) if @user_course_identity > Course::STUDENT && @course.is_public == 0 + tip_exception(401, "..") unless User.current.logged? tip_exception(409, "您没有权限进入") end uid_logger("###############user_course_identity:#{@user_course_identity}") @@ -582,4 +583,8 @@ class ApplicationController < ActionController::Base def render_parameter_missing render json: { status: -1, message: '参数缺失' } end + + def set_export_cookies + cookies[:fileDownload] = true + end end diff --git a/app/controllers/concerns/paginate_helper.rb b/app/controllers/concerns/paginate_helper.rb index 34740eb5d..bbe84a348 100644 --- a/app/controllers/concerns/paginate_helper.rb +++ b/app/controllers/concerns/paginate_helper.rb @@ -1,7 +1,7 @@ module PaginateHelper def paginate(objs, **opts) page = params[:page].to_i <= 0 ? 1 : params[:page].to_i - per_page = params[:per_page].to_i > 0 ? params[:per_page].to_i : opts[:per_page] || 20 + per_page = params[:per_page].to_i > 0 && params[:per_page].to_i < 50 ? params[:per_page].to_i : opts[:per_page] || 20 Kaminari.paginate_array(objs).page(page).per(per_page) end diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb index bdb367111..9ae909dbc 100644 --- a/app/controllers/courses_controller.rb +++ b/app/controllers/courses_controller.rb @@ -1029,8 +1029,8 @@ class CoursesController < ApplicationController normal_status(-1,"课堂暂时没有学生") else member_to_xlsx(@course, @all_members, @c_homeworks, @c_exercises, @c_tasks) - filename_ = "#{current_user.real_name}_#{@course.name}_全部成绩" - render xlsx: "#{format_sheet_name filename_.strip.first(30)}",template: "courses/export_member_scores_excel.xlsx.axlsx", + filename_ = "#{current_user.real_name}_#{@course.name}_全部成绩_#{Time.now.strftime('%Y%m%d_%H%M%S')}" + render xlsx: "#{format_sheet_name filename_.strip}",template: "courses/export_member_scores_excel.xlsx.axlsx", locals: {course_info:@course_info, activity_level:@user_activity_level, course_scores:@course_user_scores,shixun_works:@shixun_work_arrays, common_works:@common_work_arrays,group_works:@group_work_arrays,task_works:@task_work_arrays, diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb index 03c943f8d..915a4e22c 100644 --- a/app/controllers/exercises_controller.rb +++ b/app/controllers/exercises_controller.rb @@ -1256,13 +1256,15 @@ class ExercisesController < ApplicationController normal_status(-1,"试卷未发布") elsif (@exercise_users_size == 0) || ( @export_ex_users&.exercise_user_committed.size == 0) normal_status(-1,"暂无用户提交") + elsif params[:export].present? && params[:export] + normal_status(0,"正在下载中") else respond_to do |format| format.xlsx{ get_export_users(@exercise,@course,@export_ex_users) exercise_export_name_ = "#{current_user.real_name}_#{@course.name}_#{@exercise.exercise_name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}" - render xlsx: "#{exercise_export_name_.strip.first(30)}",template: "exercises/exercise_lists.xlsx.axlsx",locals: {table_columns:@table_columns,exercise_users:@user_columns} + render xlsx: "#{exercise_export_name_.strip}",template: "exercises/exercise_lists.xlsx.axlsx",locals: {table_columns:@table_columns,exercise_users:@user_columns} } end end @@ -1281,7 +1283,11 @@ class ExercisesController < ApplicationController @exercise_questions = @exercise.exercise_questions.includes(:exercise_choices).order("question_number ASC") filename_ = "#{@exercise.user.real_name}_#{@course.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}.pdf" stylesheets = "#{Rails.root}/app/templates/exercise_export/exercise_export.css" - render pdf: 'exercise_export/blank_exercise', filename: filename_, stylesheets: stylesheets + if params[:export].present? && params[:export] + normal_status(0,"正在下载中") + else + render pdf: 'exercise_export/blank_exercise', filename: filename_, stylesheets: stylesheets + end end #空白试卷预览页面,仅供测试使用,无其他任何用途 diff --git a/app/controllers/forums_controller.rb b/app/controllers/forums_controller.rb new file mode 100644 index 000000000..82573fdd0 --- /dev/null +++ b/app/controllers/forums_controller.rb @@ -0,0 +1,2 @@ +class ForumsController < ApplicationController +end diff --git a/app/controllers/graduation_tasks_controller.rb b/app/controllers/graduation_tasks_controller.rb index ab99e8d86..aeb5c409d 100644 --- a/app/controllers/graduation_tasks_controller.rb +++ b/app/controllers/graduation_tasks_controller.rb @@ -137,7 +137,7 @@ class GraduationTasksController < ApplicationController format.xlsx{ graduation_work_to_xlsx(@work_excel,@task,current_user) task_export_name_ = "#{current_user.real_name}_#{@course.name}_#{@task.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}" - render xlsx: "#{task_export_name_.strip.first(30)}",template: "graduation_tasks/tasks_list.xlsx.axlsx",locals: {table_columns:@head_cells_column, task_users:@task_cells_column} + render xlsx: "#{task_export_name_.strip}",template: "graduation_tasks/tasks_list.xlsx.axlsx",locals: {table_columns:@head_cells_column, task_users:@task_cells_column} } end end diff --git a/app/controllers/graduation_topics_controller.rb b/app/controllers/graduation_topics_controller.rb index 754b472c5..96816c2a6 100644 --- a/app/controllers/graduation_topics_controller.rb +++ b/app/controllers/graduation_topics_controller.rb @@ -271,7 +271,7 @@ class GraduationTopicsController < ApplicationController students = course.students.joins(user: :user_extension).order("user_extensions.student_id") graduation_topic_to_xlsx(students,course) topic_export_name_ = "#{current_user.real_name}_#{course.name}_毕设选题_#{Time.now.strftime('%Y%m%d_%H%M%S')}" - render xlsx: "#{topic_export_name_.strip.first(30)}",template: "graduation_topics/export.xlsx.axlsx",locals: {table_columns:@topic_head_cells,topic_users:@topic_body_cells} + 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 uid_logger(e.message) missing_template diff --git a/app/controllers/homework_commons_controller.rb b/app/controllers/homework_commons_controller.rb index 5cccb3ee7..f7906ecda 100644 --- a/app/controllers/homework_commons_controller.rb +++ b/app/controllers/homework_commons_controller.rb @@ -207,9 +207,12 @@ class HomeworkCommonsController < ApplicationController tip_exception(403, "无权限操作") elsif @work_excel.blank? || @work_excel.size == 0 normal_status(-1,"暂无用户提交!") + elsif params[:export].present? && params[:export] + normal_status(0,"正在下载中") else respond_to do |format| format.xlsx{ + set_export_cookies student_work_to_xlsx(@work_excel,@homework) exercise_export_name = "#{current_user.real_name}_#{@course.name}_#{@homework.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}" render xlsx: "#{exercise_export_name.strip}",template: "homework_commons/works_list.xlsx.axlsx",locals: @@ -229,12 +232,17 @@ class HomeworkCommonsController < ApplicationController end if status == 0 - respond_to do |format| - format.zip{ - zipfile = zip_homework_common @homework, zip_works - file = decode64(zipfile[0][:base64file]) - send_file "#{OUTPUT_FOLDER}/#{file}", filename: filename_for_content_disposition(file), type: 'application/zip' - } + if params[:export].present? && params[:export] + normal_status(0,"正在下载中") + else + respond_to do |format| + format.zip{ + set_export_cookies + zipfile = zip_homework_common @homework, zip_works + file = decode64(zipfile[0][:base64file]) + send_file "#{OUTPUT_FOLDER}/#{file}", filename: filename_for_content_disposition(file), type: 'application/zip' + } + end end else normal_status(status, status == -2 ? "500M" : "无附件可下载") diff --git a/app/controllers/memos_controller.rb b/app/controllers/memos_controller.rb index 723f2e9a4..869d787db 100644 --- a/app/controllers/memos_controller.rb +++ b/app/controllers/memos_controller.rb @@ -66,9 +66,7 @@ class MemosController < ApplicationController # GET /memos/new def new - @csrf_token = session[:_csrf_toke] ||= SecureRandom.base64(32) @tag_list = TagRepertoire.field_for_list.order("name asc") - end # GET /memos/1/edit diff --git a/app/controllers/polls_controller.rb b/app/controllers/polls_controller.rb index 4348b3bfc..87435f743 100644 --- a/app/controllers/polls_controller.rb +++ b/app/controllers/polls_controller.rb @@ -950,7 +950,7 @@ class PollsController < ApplicationController format.xlsx{ polls_export_name_ = "#{current_user.real_name}_#{@course.name}_#{@poll.polls_name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}" polls_user_commit = poll_commit_result(@poll,@poll_export_questions,@poll_users,@poll_commit_ids) - render xlsx: "#{polls_export_name_.strip.first(30)}",template: "polls/commit_result.xlsx.axlsx",locals: {polls_user_commit:polls_user_commit} + render xlsx: "#{polls_export_name_.strip}",template: "polls/commit_result.xlsx.axlsx",locals: {polls_user_commit:polls_user_commit} } end end diff --git a/app/controllers/shixuns_controller.rb b/app/controllers/shixuns_controller.rb index 6c3f34a60..26effd12e 100644 --- a/app/controllers/shixuns_controller.rb +++ b/app/controllers/shixuns_controller.rb @@ -714,6 +714,7 @@ class ShixunsController < ApplicationController end end rescue Exception => e + logger.info("shixun_exec error: #{e.message}") if e.message != "ActiveRecord::RecordInvalid" logger.error("##########project_fork error #{e.message}") @myshixun.destroy! diff --git a/app/controllers/student_works_controller.rb b/app/controllers/student_works_controller.rb index f9f4717a6..a5d46e46a 100644 --- a/app/controllers/student_works_controller.rb +++ b/app/controllers/student_works_controller.rb @@ -457,8 +457,8 @@ class StudentWorksController < ApplicationController @myself_eff = @echart_data[:efficiency_list].find { |item| item.last == @user.id } @myself_consume = @echart_data[:consume_list].find { |item| item.last == @user.id } - filename_ = "实训报告_#{@shixun&.name}_#{@use&.real_name}" - filename = Base64.urlsafe_encode64(filename_.strip.first(30)) + filename_ = "#{@use&.student_id}_#{@use&.real_name}_#{@shixun&.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}" + filename = Base64.urlsafe_encode64(filename_.strip) stylesheets = %w(shixun_work/shixun_work.css shared/codemirror.css) render pdf: 'shixun_work/shixun_work', filename: filename, stylesheets: stylesheets end diff --git a/app/controllers/users/base_controller.rb b/app/controllers/users/base_controller.rb index afc03ee13..fd138a182 100644 --- a/app/controllers/users/base_controller.rb +++ b/app/controllers/users/base_controller.rb @@ -26,9 +26,22 @@ class Users::BaseController < ApplicationController render_forbidden end + def page_value + params[:page].to_i <= 0 ? 1 : params[:page].to_i + end + + def per_page_value + params[:per_page].to_i > 0 && params[:per_page].to_i < 50 ? params[:per_page].to_i : 20 + end + alias_method :limit_value, :per_page_value + + def offset_value + (page_value - 1) * limit_value + end + def paginate(objs, **opts) - page = params[:page].to_i <= 0 ? 1 : params[:page].to_i - per_page = params[:per_page].to_i > 0 ? params[:per_page].to_i : 20 + page = page_value + per_page = per_page_value return Kaminari.paginate_array(objs).page(page).per(per_page) unless observed_logged_user? && opts[:special] diff --git a/app/controllers/users/private_message_details_controller.rb b/app/controllers/users/private_message_details_controller.rb new file mode 100644 index 000000000..486d23d7f --- /dev/null +++ b/app/controllers/users/private_message_details_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/users/private_messages_controller.rb b/app/controllers/users/private_messages_controller.rb new file mode 100644 index 000000000..b80b6152b --- /dev/null +++ b/app/controllers/users/private_messages_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/users/recent_contacts_controller.rb b/app/controllers/users/recent_contacts_controller.rb new file mode 100644 index 000000000..bc4b8ea8f --- /dev/null +++ b/app/controllers/users/recent_contacts_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/users/unread_message_infos_controller.rb b/app/controllers/users/unread_message_infos_controller.rb new file mode 100644 index 000000000..7abd36304 --- /dev/null +++ b/app/controllers/users/unread_message_infos_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/users_for_private_messages_controller.rb b/app/controllers/users_for_private_messages_controller.rb new file mode 100644 index 000000000..bbd5682a1 --- /dev/null +++ b/app/controllers/users_for_private_messages_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/zips_controller.rb b/app/controllers/zips_controller.rb index f4822b96b..b1f7804a9 100644 --- a/app/controllers/zips_controller.rb +++ b/app/controllers/zips_controller.rb @@ -8,7 +8,13 @@ class ZipsController < ApplicationController def shixun_report service = BatchExportShixunReportService.new(@homework, @all_student_works) filename_ = filename_for_content_disposition(service.filename) - send_file service.zip, filename: filename_, type: 'application/zip' + if params[:export].present? && params[:export] + normal_status(0,"正在下载中") + else + set_export_cookies + send_file service.zip, filename: filename_, type: 'application/zip' + end + rescue BatchExportShixunReportService::Error => ex normal_status(-1, ex.message) end @@ -18,7 +24,11 @@ class ZipsController < ApplicationController exercises = ExportExercisesService.new(@exercise,@ex_users,@request_url) file_name_ = filename_for_content_disposition(exercises.filename) - send_file exercises.ex_zip, filename: file_name_, type: 'application/zip' + if params[:export].present? && params[:export] + normal_status(0,"正在下载中") + else + send_file exercises.ex_zip, filename: file_name_, type: 'application/zip' + end rescue Exception => e normal_status(-1, e.message) end diff --git a/app/decorators/private_message_decorator.rb b/app/decorators/private_message_decorator.rb new file mode 100644 index 000000000..6db17acf3 --- /dev/null +++ b/app/decorators/private_message_decorator.rb @@ -0,0 +1,9 @@ +module PrivateMessageDecorator + extend ApplicationDecorator + + display_time_method :send_time + + def unread? + status.zero? + end +end \ No newline at end of file diff --git a/app/helpers/export_helper.rb b/app/helpers/export_helper.rb index 742deb85b..5d36c465f 100644 --- a/app/helpers/export_helper.rb +++ b/app/helpers/export_helper.rb @@ -418,7 +418,7 @@ module ExportHelper end end - out_file_name = "#{Time.now.to_i}_#{homework_common.name}.zip" + out_file_name = "作品附件_#{homework_common&.course&.name}_#{homework_common.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}.zip" out_file_name.gsub!(" ", "-") out_file_name.gsub!("/", "_") out_file = find_or_pack(homework_common, homework_common.user_id, digests.sort){ @@ -496,8 +496,8 @@ module ExportHelper def make_zip_name(work, file_name="") Rails.logger.info("######################file_name: #{file_name}") - name = file_name === "" ? "" : (file_name[0, file_name.rindex('.')]+"_") - "#{name}#{work.user.real_name}_#{((work.user.student_id.nil?) ? "" : work.user.student_id)}" + # name = file_name === "" ? "" : (file_name[0, file_name.rindex('.')]+"_") + "#{work&.user&.student_id}_#{work&.user&.real_name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}" end def zipping(zip_name_refer, files_paths, output_path, is_attachment=false, not_exist_file=[]) diff --git a/app/helpers/forums_helper.rb b/app/helpers/forums_helper.rb new file mode 100644 index 000000000..2e531fd46 --- /dev/null +++ b/app/helpers/forums_helper.rb @@ -0,0 +1,2 @@ +module ForumsHelper +end diff --git a/app/helpers/memos_helper.rb b/app/helpers/memos_helper.rb index 7df887a4b..434d9b66a 100644 --- a/app/helpers/memos_helper.rb +++ b/app/helpers/memos_helper.rb @@ -1,2 +1,6 @@ module MemosHelper + + def forum_list + [{id: 5, name: "技术分享"}, {id: 3, name: "操作指南"}] + end end diff --git a/app/models/forum.rb b/app/models/forum.rb new file mode 100644 index 000000000..88aafa676 --- /dev/null +++ b/app/models/forum.rb @@ -0,0 +1,2 @@ +class Forum < ApplicationRecord +end diff --git a/app/models/memo.rb b/app/models/memo.rb index 610a7684d..4dc153c81 100644 --- a/app/models/memo.rb +++ b/app/models/memo.rb @@ -1,7 +1,9 @@ class Memo < ApplicationRecord include Searchable::Memo - has_many :memo_tag_repertoires, :dependent => :destroy + belongs_to :forum, touch: true + + has_many :memo_tag_repertoires, dependent: :destroy has_many :tag_repertoires, :through => :memo_tag_repertoires has_many :praise_tread, as: :praise_tread_object, dependent: :destroy diff --git a/app/models/private_message.rb b/app/models/private_message.rb index 1db4c9f66..640e48db7 100644 --- a/app/models/private_message.rb +++ b/app/models/private_message.rb @@ -1,3 +1,9 @@ class PrivateMessage < ApplicationRecord belongs_to :user + belongs_to :target, class_name: "User" + belongs_to :sender, class_name: "User" + belongs_to :receiver, class_name: "User" + + scope :without_deleted, -> { where.not(status: 2) } + scope :only_unread, -> { where(status: 0) } end diff --git a/app/models/tiding.rb b/app/models/tiding.rb index 3ef625c57..90abdf809 100644 --- a/app/models/tiding.rb +++ b/app/models/tiding.rb @@ -13,9 +13,14 @@ class Tiding < ApplicationRecord value = container.try(:identifier) end + if value.blank? && parent_container_type && Object.const_defined?(parent_container_type) + value = parent_container_type.try(:identifier) + end + if value.blank? && belong_container_type && Object.const_defined?(belong_container_type) value = belong_container.try(:identifier) end + value end end \ No newline at end of file diff --git a/app/models/user.rb b/app/models/user.rb index 14d7b2697..ed9d70c00 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -54,7 +54,8 @@ class User < ApplicationRecord has_one :onclick_time, :dependent => :destroy # 新版私信 - has_many :private_messages, :dependent => :destroy + has_many :private_messages, dependent: :destroy + has_many :recent_contacts, through: :private_messages, source: :target has_many :tidings, :dependent => :destroy has_many :games, :dependent => :destroy diff --git a/app/services/batch_export_shixun_report_service.rb b/app/services/batch_export_shixun_report_service.rb index d71235790..253eb4480 100644 --- a/app/services/batch_export_shixun_report_service.rb +++ b/app/services/batch_export_shixun_report_service.rb @@ -14,7 +14,7 @@ class BatchExportShixunReportService end def filename - @_filename ||= "#{homework.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}" + @_filename ||= "实训报告_#{homework&.course&.name}_#{homework.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}" end def zip diff --git a/app/services/exercise_user_pdf_service.rb b/app/services/exercise_user_pdf_service.rb index 73711192f..6891e53ea 100644 --- a/app/services/exercise_user_pdf_service.rb +++ b/app/services/exercise_user_pdf_service.rb @@ -15,8 +15,8 @@ class ExerciseUserPdfService end def filename - user_course = @course.course_members.find_by(user_id:@ex_user_user.id)&.course_group_name - exercise_user_name = "#{@ex_user_user.real_name}_#{user_course.present? ? user_course : "未分班"}_#{exercise.exercise_name}_#{Time.now.strftime('%Y%m%d_%H%M')}" + # user_course = @course.course_members.find_by(user_id:@ex_user_user.id)&.course_group_name + exercise_user_name = "#{@ex_user_user&.student_id}_#{@ex_user_user.real_name}_#{exercise.exercise_name}_#{Time.now.strftime('%Y%m%d_%H%M')}" "#{exercise_user_name.strip}.pdf" end diff --git a/app/services/export_exercises_service.rb b/app/services/export_exercises_service.rb index ca2d347a6..1f1e15326 100644 --- a/app/services/export_exercises_service.rb +++ b/app/services/export_exercises_service.rb @@ -10,7 +10,7 @@ class ExportExercisesService end def filename - exercise_export_name = "#{exercise.user.real_name}_#{exercise.exercise_name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}" + exercise_export_name = "学生答题_#{exercise&.course&.name}_#{exercise.exercise_name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}" "#{exercise_export_name.strip}.zip" end diff --git a/app/services/export_shixun_report_service.rb b/app/services/export_shixun_report_service.rb index f9da3d6f2..57b955762 100644 --- a/app/services/export_shixun_report_service.rb +++ b/app/services/export_shixun_report_service.rb @@ -10,7 +10,7 @@ class ExportShixunReportService end def filename - @_filename ||= "#{homework.name}-#{work.user&.student_id}-#{work.user.real_name}.pdf".gsub(' ', '-').gsub('/', '_') + @_filename ||= "#{work.user&.student_id}_#{work.user.real_name}_#{homework.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}.pdf".gsub(' ', '-').gsub('/', '_') end def prepare_binding diff --git a/app/services/private_messages/create_service.rb b/app/services/private_messages/create_service.rb new file mode 100644 index 000000000..560f1a540 --- /dev/null +++ b/app/services/private_messages/create_service.rb @@ -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 \ No newline at end of file diff --git a/app/services/project_packages/save_service.rb b/app/services/project_packages/save_service.rb index bcfc19a10..a876f56b3 100644 --- a/app/services/project_packages/save_service.rb +++ b/app/services/project_packages/save_service.rb @@ -15,7 +15,7 @@ class ProjectPackages::SaveService < ApplicationService is_create = package.new_record? raise Error, '类型不存在' unless ProjectPackageCategory.where(id: params[:category_id]).exists? - params[:project_package_category_id] = params[:category_id].to_i + params[:project_package_category_id] = params.delete(:category_id).to_i raise Error, '竞标截止时间不能小于当前时间' if params[:deadline_at].present? && params[:deadline_at].to_time < Time.now @@ -25,7 +25,9 @@ class ProjectPackages::SaveService < ApplicationService end ActiveRecord::Base.transaction do - package.assign_attributes(params) + columns = %i[project_package_category_id title content deadline_at + min_price max_price contact_name contact_phone] + package.assign_attributes(params.slice(*columns)) package.save! # 处理附件 diff --git a/app/views/courses/search_users.json.jbuilder b/app/views/courses/search_users.json.jbuilder index ac826fdf2..61362de66 100644 --- a/app/views/courses/search_users.json.jbuilder +++ b/app/views/courses/search_users.json.jbuilder @@ -1,6 +1,7 @@ json.users do json.array! @users do |user| json.id user.id + json.login user.login json.name user.real_name json.student_id user&.student_id json.school_name user&.school_name diff --git a/app/views/memos/new.json.jbuilder b/app/views/memos/new.json.jbuilder index 14313b2ac..47c023ea0 100644 --- a/app/views/memos/new.json.jbuilder +++ b/app/views/memos/new.json.jbuilder @@ -1,3 +1,3 @@ json.tag_list @tag_list -json.csrf_token @csrf_token +json.forums @csrf_token diff --git a/app/views/users/get_user_info.json.jbuilder b/app/views/users/get_user_info.json.jbuilder index d9eb736ad..d12b950f9 100644 --- a/app/views/users/get_user_info.json.jbuilder +++ b/app/views/users/get_user_info.json.jbuilder @@ -7,6 +7,7 @@ json.is_teacher @user.user_extension&.teacher? json.user_identity @user.identity json.tidding_count 0 json.user_phone_binded @user.phone.present? +json.phone @user.phone json.profile_completed @user.profile_completed? if @course json.course_identity @course_identity diff --git a/app/views/users/private_message_details/show.json.jbuilder b/app/views/users/private_message_details/show.json.jbuilder new file mode 100644 index 000000000..065767fb4 --- /dev/null +++ b/app/views/users/private_message_details/show.json.jbuilder @@ -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 \ No newline at end of file diff --git a/app/views/users/private_messages/create.json.jbuilder b/app/views/users/private_messages/create.json.jbuilder new file mode 100644 index 000000000..888cfeff6 --- /dev/null +++ b/app/views/users/private_messages/create.json.jbuilder @@ -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 \ No newline at end of file diff --git a/app/views/users/private_messages/index.json.jbuilder b/app/views/users/private_messages/index.json.jbuilder new file mode 100644 index 000000000..c18a7d209 --- /dev/null +++ b/app/views/users/private_messages/index.json.jbuilder @@ -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 \ No newline at end of file diff --git a/app/views/users/recent_contacts/index.json.jbuilder b/app/views/users/recent_contacts/index.json.jbuilder new file mode 100644 index 000000000..f2f7f025a --- /dev/null +++ b/app/views/users/recent_contacts/index.json.jbuilder @@ -0,0 +1,2 @@ +json.users @contacts, partial: 'users/user_simple', as: :user +json.count @contacts.size \ No newline at end of file diff --git a/app/views/users_for_private_messages/index.json.jbuilder b/app/views/users_for_private_messages/index.json.jbuilder new file mode 100644 index 000000000..be040e368 --- /dev/null +++ b/app/views/users_for_private_messages/index.json.jbuilder @@ -0,0 +1,2 @@ +json.users @users, partial: 'users/user_simple', as: :user +json.count @users.size \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 6623edd99..2f9eed61b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -53,6 +53,11 @@ Rails.application.routes.draw do resource :grade_records, only: [:show] resource :watch, only: [:create, :destroy] resources :project_packages, only: [:index] + # 私信 + resources :private_messages, only: [:index, :create, :destroy] + resources :recent_contacts, only: [:index] + resource :private_message_details, only: [:show] + resource :unread_message_info, only: [:show] end @@ -91,6 +96,7 @@ Rails.application.routes.draw do end end end + resources :users_for_private_messages, only: [:index] resources :myshixuns, param: :identifier, shallow: true do member do diff --git a/db/migrate/20190730024112_add_index_to_user.rb b/db/migrate/20190730024112_add_index_to_user.rb new file mode 100644 index 000000000..fa198cffa --- /dev/null +++ b/db/migrate/20190730024112_add_index_to_user.rb @@ -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 diff --git a/public/compatibility.html b/public/compatibility.html index f13c673f5..f28bad2c8 100644 --- a/public/compatibility.html +++ b/public/compatibility.html @@ -5,9 +5,9 @@ EduCoder - - - + + + diff --git a/public/images/educoder/path.png b/public/images/educoder/path.png new file mode 100644 index 000000000..d8b6f1715 Binary files /dev/null and b/public/images/educoder/path.png differ diff --git a/public/images/educoder/project_packages/ai.png b/public/images/educoder/project_packages/ai.png new file mode 100755 index 000000000..825e75165 Binary files /dev/null and b/public/images/educoder/project_packages/ai.png differ diff --git a/public/images/educoder/project_packages/backend.png b/public/images/educoder/project_packages/backend.png new file mode 100755 index 000000000..359bca746 Binary files /dev/null and b/public/images/educoder/project_packages/backend.png differ diff --git a/public/images/educoder/project_packages/cloud_compute_and_big_data.png b/public/images/educoder/project_packages/cloud_compute_and_big_data.png new file mode 100755 index 000000000..42e64da3b Binary files /dev/null and b/public/images/educoder/project_packages/cloud_compute_and_big_data.png differ diff --git a/public/images/educoder/project_packages/database.png b/public/images/educoder/project_packages/database.png new file mode 100755 index 000000000..2b668e774 Binary files /dev/null and b/public/images/educoder/project_packages/database.png differ diff --git a/public/images/educoder/project_packages/devops_and_test.png b/public/images/educoder/project_packages/devops_and_test.png new file mode 100755 index 000000000..77b8e6f3b Binary files /dev/null and b/public/images/educoder/project_packages/devops_and_test.png differ diff --git a/public/images/educoder/project_packages/front.png b/public/images/educoder/project_packages/front.png new file mode 100755 index 000000000..23ba2a755 Binary files /dev/null and b/public/images/educoder/project_packages/front.png differ diff --git a/public/images/educoder/project_packages/mobile.png b/public/images/educoder/project_packages/mobile.png new file mode 100755 index 000000000..cd73520c7 Binary files /dev/null and b/public/images/educoder/project_packages/mobile.png differ diff --git a/public/images/educoder/project_packages/other.png b/public/images/educoder/project_packages/other.png new file mode 100755 index 000000000..a700cd5de Binary files /dev/null and b/public/images/educoder/project_packages/other.png differ diff --git a/public/javascripts/download/jquery.fileDownload.js b/public/javascripts/download/jquery.fileDownload.js new file mode 100644 index 000000000..0d0301c23 --- /dev/null +++ b/public/javascripts/download/jquery.fileDownload.js @@ -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 ', so use ' 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) { + $("
").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 = $("
").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) { + $("
").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 = $("").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 += ''; + } + }); + } + + if (isOtherMobileBrowser) { + + $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 = $("").appendTo("body"); + formDoc = getiframeDocument($iframe); + } + + formDoc.write("" + formInnerHtml + "
" + settings.popupWindowTitle + ""); + $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); \ No newline at end of file diff --git a/public/javascripts/download/jquery.fileDownload.min.js b/public/javascripts/download/jquery.fileDownload.min.js new file mode 100644 index 000000000..c4ae8b923 --- /dev/null +++ b/public/javascripts/download/jquery.fileDownload.min.js @@ -0,0 +1 @@ +!function(e,o){var t=/[<>&\r\n"']/gm,a={"<":"lt;",">":"gt;","&":"amp;","\r":"#13;","\n":"#10;",'"':"quot;","'":"#39;"};e.extend({fileDownload:function(i,n){var r,l,c,d=e.extend({preparingMessageHtml:null,failMessageHtml:null,androidPostUnsupportedMessageHtml:"Unfortunately your Android browser doesn't support this type of file download. Please try again with a different browser.",dialogOptions:{modal:!0},prepareCallback:function(e){},successCallback:function(e){},abortCallback:function(e){},failCallback:function(e,o,t){},httpMethod:"GET",data:null,checkInterval:100,cookieName:"fileDownload",cookieValue:"true",cookiePath:"/",cookieDomain:null,popupWindowTitle:"Initiating file download...",encodeHTMLEntities:!0},n),s=new e.Deferred,p=(navigator.userAgent||navigator.vendor||o.opera).toLowerCase();/ip(ad|hone|od)/.test(p)?r=!0:-1!==p.indexOf("android")?l=!0:c=/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(p)||/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(p.substr(0,4));var m=d.httpMethod.toUpperCase();if(l&&"GET"!==m&&d.androidPostUnsupportedMessageHtml)return e().dialog?e("
").html(d.androidPostUnsupportedMessageHtml).dialog(d.dialogOptions):alert(d.androidPostUnsupportedMessageHtml),s.reject();var u,f,g,h,b=null,k={onPrepare:function(o){d.preparingMessageHtml?b=e("
").html(d.preparingMessageHtml).dialog(d.dialogOptions):d.prepareCallback&&d.prepareCallback(o)},onSuccess:function(e){b&&b.dialog("close"),d.successCallback(e),s.resolve(e)},onAbort:function(e){b&&b.dialog("close"),d.abortCallback(e),s.reject(e)},onFail:function(o,t,a){b&&b.dialog("close"),d.failMessageHtml&&e("
").html(d.failMessageHtml).dialog(d.dialogOptions),d.failCallback(o,t,a),s.reject(o,t)}};if(k.onPrepare(i),null!==d.data&&"string"!=typeof d.data&&(d.data=e.param(d.data)),"GET"===m){if(null!==d.data)-1!==i.indexOf("?")?"&"!==i.substring(i.length-1)&&(i+="&"):i+="?",i+=d.data;r||l?((f=o.open(i)).document.title=d.popupWindowTitle,o.focus()):c?o.location(i):u=e("").appendTo("body")}else{var v="";null!==d.data&&e.each(d.data.replace(/\+/g," ").split("&"),function(){var e=this.split("="),o=e[0];e.shift(),e=[o,e.join("=")];var t=d.encodeHTMLEntities?T(decodeURIComponent(e[0])):decodeURIComponent(e[0]);if(t){var a=d.encodeHTMLEntities?T(decodeURIComponent(e[1])):decodeURIComponent(e[1]);v+=''}}),c?(h=e("
").appendTo("body")).hide().prop("method",d.httpMethod).prop("action",i).html(v):(r?((f=o.open("about:blank")).document.title=d.popupWindowTitle,g=f.document,o.focus()):g=w(u=e("").appendTo("body")),g.write(""+v+"
"+d.popupWindowTitle+""),h=e(g).find("form")),h.submit()}function w(e){var o=e[0].contentWindow||e[0].contentDocument;return o.document&&(o=o.document),o}function y(e){setTimeout(function(){f&&(l&&f.close(),r&&f.focus&&(f.focus(),e&&f.close()))},0)}function T(e){return e.replace(t,function(e){return"&"+a[e]})}setTimeout(function o(){var t=d.cookieValue;"string"==typeof t&&(t=t.toLowerCase());var a=d.cookieName.toLowerCase()+"="+t;if(document.cookie.toLowerCase().indexOf(a)>-1){k.onSuccess(i);var n=d.cookieName+"=; path="+d.cookiePath+"; expires="+new Date(0).toUTCString()+";";return d.cookieDomain&&(n+=" domain="+d.cookieDomain+";"),document.cookie=n,void y(!1)}if(f||u)try{var r=f?f.document:w(u);if(r&&null!==r.body&&r.body.innerHTML.length){var l=!0;if(h&&h.length){var c=e(r.body).contents().first();try{c.length&&c[0]===h[0]&&(l=!1)}catch(e){if(!e||-2146828218!=e.number)throw e;l=!0}}if(l)return void setTimeout(function(){k.onFail(r.body.innerHTML,i),y(!0)},100)}}catch(e){return k.onFail("",i,e),void y(!0)}setTimeout(o,d.checkInterval)},d.checkInterval);var x=s.promise();return x.abort=function(){y(),u.attr("src","").html(""),k.onAbort(i)},x}})}(jQuery,this||window); \ No newline at end of file diff --git a/public/react/public/css/edu-all.css b/public/react/public/css/edu-all.css index 8b7c727a9..fad3568b4 100644 --- a/public/react/public/css/edu-all.css +++ b/public/react/public/css/edu-all.css @@ -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%;} .path-nav li{float: left;padding: 0px 30px;height: 42px;} .path-nav li a{color:#fff;font-size: 16px;display: block; height: 40px;} diff --git a/public/react/src/AppConfig.js b/public/react/src/AppConfig.js index 2ad7df0d5..363ff495d 100644 --- a/public/react/src/AppConfig.js +++ b/public/react/src/AppConfig.js @@ -41,7 +41,7 @@ export function initAxiosInterceptors(props) { // proxy = "http://testbdweb.trustie.net" // proxy = "http://testbdweb.educoder.net" // proxy = "https://testeduplus2.educoder.net" - proxy="http://47.96.87.25:48080/" + proxy="http://47.96.87.25:48080" // 在这里使用requestMap控制,避免用户通过双击等操作发出重复的请求; diff --git a/public/react/src/common/TextUtil.js b/public/react/src/common/TextUtil.js index 8aef0adbd..bb0b3f145 100644 --- a/public/react/src/common/TextUtil.js +++ b/public/react/src/common/TextUtil.js @@ -1,4 +1,6 @@ -import { bytesToSize } from 'educoder'; +import { bytesToSize, getUrl2 } from 'educoder'; +const $ = window.$ + export function isImageExtension(fileName) { return fileName ? !!(fileName.match(/.(jpg|jpeg|png|gif)$/i)) : false } @@ -36,6 +38,25 @@ export function markdownToHTML(oldContent, selector) { return content } } +function _doDownload(options) { + $.fileDownload("/api" + options.url, { + successCallback: options.successCallback, + failCallback: options.failCallback + }); +} +export function downloadFile(options) { + if ($.fileDownload) { + _doDownload(options) + } else { + const _url_origin = getUrl2() + $.getScript( + `${_url_origin}/javascripts/download/jquery.fileDownload.min.js`, + (data, textStatus, jqxhr) => { + _doDownload(options) + }); + } + +} export function appendFileSizeToUploadFile(item) { return `${item.title}${uploadNameSizeSeperator}${item.filesize}` diff --git a/public/react/src/common/educoder.js b/public/react/src/common/educoder.js index f090d2c39..44b3057cc 100644 --- a/public/react/src/common/educoder.js +++ b/public/react/src/common/educoder.js @@ -15,7 +15,8 @@ export { updatePageParams as updatePageParams } from './RouterUti export { bytesToSize as bytesToSize } from './UnitUtil'; -export { markdownToHTML, uploadNameSizeSeperator, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll, isImageExtension } from './TextUtil' +export { markdownToHTML, uploadNameSizeSeperator, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll, isImageExtension, + downloadFile } from './TextUtil' export { handleDateString, getNextHalfHourOfMoment,formatDuring } from './DateUtil' diff --git a/public/react/src/modules/courses/busyWork/CommonWorkDetailIndex.js b/public/react/src/modules/courses/busyWork/CommonWorkDetailIndex.js index e34783933..2a211007b 100644 --- a/public/react/src/modules/courses/busyWork/CommonWorkDetailIndex.js +++ b/public/react/src/modules/courses/busyWork/CommonWorkDetailIndex.js @@ -8,7 +8,7 @@ import { CNotificationHOC } from '../common/CNotificationHOC' import { RouteHOC } from './common' import locale from 'antd/lib/date-picker/locale/zh_CN'; -import { WordsBtn, MarkdownToHtml, trigger, queryString } from 'educoder'; +import { WordsBtn, MarkdownToHtml, trigger, queryString, downloadFile } from 'educoder'; import axios from 'axios'; import Modals from '../../modals/Modals'; import CoursesListType from '../coursesPublic/CoursesListType'; @@ -18,6 +18,8 @@ import '../css/Courses.css' import CBreadcrumb from '../common/CBreadcrumb' import DownloadMessageysl from "../../modals/DownloadMessageysl"; +import { Spin } from 'antd' + //引入对应跳转的组件 //新建分组/普通作业 @@ -103,7 +105,7 @@ class CommonWorkDetailIndex extends Component{ /// 确认是否下载 confirmysl(url,params){ - axios.get(url,{ + axios.get(url+ '&export=true',{ params }).then((response) => { if(response.data.status&&response.data.status===-1){ @@ -125,7 +127,21 @@ class CommonWorkDetailIndex extends Component{ } }else { 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) => { console.log(error) @@ -250,7 +266,7 @@ class CommonWorkDetailIndex extends Component{ }
- { noTab !== true &&
+ { noTab !== true &&
this.setState({moduleName: '作品列表'})} @@ -289,18 +305,30 @@ class CommonWorkDetailIndex extends Component{ padding-top: 10px; padding-bottom: 8px; } + + .floatSpinParent .ant-spin-nested-loading { + float: right; + } `} - {this.props.isAdmin()?
  • + {this.props.isAdmin()? +
  • 导出 -
  • :""} + + :""} {/* {isAdmin && 导出作品附件} {isAdmin && 导出成绩} */} diff --git a/public/react/src/modules/courses/exercise/Testpapersettinghomepage.js b/public/react/src/modules/courses/exercise/Testpapersettinghomepage.js index 333824538..ec7fc189c 100644 --- a/public/react/src/modules/courses/exercise/Testpapersettinghomepage.js +++ b/public/react/src/modules/courses/exercise/Testpapersettinghomepage.js @@ -276,7 +276,7 @@ class Testpapersettinghomepage extends Component{

    {this.props.coursedata.name} > - 试卷 + 试卷 > 试卷详情

    diff --git a/public/react/src/modules/modals/DownloadMessage.js b/public/react/src/modules/modals/DownloadMessage.js index 2822614de..b7796316c 100644 --- a/public/react/src/modules/modals/DownloadMessage.js +++ b/public/react/src/modules/modals/DownloadMessage.js @@ -18,7 +18,7 @@ class DownloadMessage extends Component { setDownload=()=>{ this.modalCancel(); - window.open(`/users/${this.props.user.login}/private_messages`) + window.open(`/users/${this.props.user.login}/message_detail?user_id=1`) } modalCancel = () => { this.setState({ diff --git a/public/react/src/modules/modals/DownloadMessageysl.js b/public/react/src/modules/modals/DownloadMessageysl.js index 3f81a93a6..b5c23960a 100644 --- a/public/react/src/modules/modals/DownloadMessageysl.js +++ b/public/react/src/modules/modals/DownloadMessageysl.js @@ -11,7 +11,7 @@ class DownloadMessageysl extends Component { setDownload=()=>{ 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() { diff --git a/public/react/src/modules/projectPackages/PackageIndex/PackageConcent.js b/public/react/src/modules/projectPackages/PackageIndex/PackageConcent.js index 46e36f90f..cfc54bb98 100644 --- a/public/react/src/modules/projectPackages/PackageIndex/PackageConcent.js +++ b/public/react/src/modules/projectPackages/PackageIndex/PackageConcent.js @@ -6,29 +6,30 @@ import moment from 'moment'; import '../packageconcnet.css'; const { Search } = Input; -// let categorylist=[ -// {name:"全部",value:undefined}, -// {name:"前端开发",value:"front"}, -// {name:"后端开发",value:"backend"}, -// {name:"移动开发",value:"mobile"}, -// {name:"数据库",value:"database"}, -// {name:"云计算和大数据",value:"cloud_compute_and_big_data"}, -// {name:"人工智能",value:"ai"}, -// {name:"其他",value:"other"}, -// ] +let categorylist=[ + {name:"全部",value:undefined}, + {name:"前端开发",value:"front"}, + {name:"后端开发",value:"backend"}, + {name:"移动开发",value:"mobile"}, + {name:"数据库",value:"database"}, + {name:"云计算和大数据",value:"cloud_compute_and_big_data"}, + {name:"人工智能",value:"ai"}, + {name:"运维与测试",value:"devops_and_test"}, + {name:"其他",value:"other"}, +] // -// function setcategorylist(val){ -// let vals="" -// categorylist.some((item,key)=> { -// if (item.value === val) { -// vals=item.name -// return true -// } -// } -// ) -// -// return vals -// } +function setcategorylist(val){ + let vals="" + categorylist.some((item,key)=> { + if (item.name === val) { + vals=item.value + return true + } + } + ) + + return vals +} @@ -157,7 +158,7 @@ class PackageConcent extends Component { enterButton={ 搜索} onSearch={ (value)=>this.setdatafuns(value)} />

    @@ -214,7 +215,7 @@ class PackageConcent extends Component {
    - +
    @@ -223,7 +224,7 @@ class PackageConcent extends Component { diff --git a/public/react/src/modules/projectPackages/PackageIndexNEITaskDetails/PackageIndexNEITaskDetails.js b/public/react/src/modules/projectPackages/PackageIndexNEITaskDetails/PackageIndexNEITaskDetails.js index 4f06a0cba..ac7c8a1a5 100644 --- a/public/react/src/modules/projectPackages/PackageIndexNEITaskDetails/PackageIndexNEITaskDetails.js +++ b/public/react/src/modules/projectPackages/PackageIndexNEITaskDetails/PackageIndexNEITaskDetails.js @@ -2,7 +2,7 @@ import React, {Component} from 'react'; import {Link} from "react-router-dom"; import axios from 'axios'; 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 moment from 'moment'; import '../packageconcnet.css'; @@ -229,15 +229,16 @@ class PackageIndexNEITaskDetails extends Component { } goback = () => { - window.history.go(-1) + // window.history.go(-1) + window.location.href="/crowdsourcings"; } render() { let {overtype,data}=this.state; // console.log(data&&data.creator.login) - // console.log(this.props.current_user.login) + console.log(data) return ( -
    + data===undefined?"":
    '} className={"fl"}> {/*{this.props.current_user.username}*/} - 众包创新 + 众包创新 {data&&data.title} @@ -264,7 +265,7 @@ class PackageIndexNEITaskDetails extends Component {
    -
    +
    {data&&data.status==="pending"?
    @@ -280,18 +281,19 @@ class PackageIndexNEITaskDetails extends Component {
    + src={`/images/${data&&data.creator.image_url}`} + width="70"/>
    {data&&data.creator.name}
    - {data&&data.creator.login===this.props.current_user.login?"":
    - {overtype===false? 头像联系TA: - 头像 头像联系TA: + 头像联系TA}
    }
    @@ -351,7 +353,7 @@ class PackageIndexNEITaskDetails extends Component { 需求详情: {data&&data.status==="pending"&&data&&data.operation.can_select_bidding_user===true?:""} @@ -393,18 +395,18 @@ class PackageIndexNEITaskDetails extends Component {
    -
    -
    -
    +
    +
    +
    {data&&data.bidding_users.map((item,key)=>{ return( -
    +
    {item.status==="bidding_won"?:""} - +

    {item.name}

    - {this.props.current_user.login!=item.login? + {this.props.current_user&&this.props.current_user.login!=item.login? 头像联系TA :""}
    @@ -442,15 +444,15 @@ class PackageIndexNEITaskDetails extends Component {
    -
    -
    -
    +
    +
    +
    {data&&data.bidding_users.map((item,key)=>{ return( -
    this.Clickteacher2(item.id)} key={key}> +
    this.Clickteacher2(item.id)} key={key}> {item.bool===true?:} - +

    {item.name}

    ) diff --git a/public/react/src/modules/projectPackages/PackageIndexNEITaskDetails/pds.css b/public/react/src/modules/projectPackages/PackageIndexNEITaskDetails/pds.css index a6f9e10dc..504a78bed 100644 --- a/public/react/src/modules/projectPackages/PackageIndexNEITaskDetails/pds.css +++ b/public/react/src/modules/projectPackages/PackageIndexNEITaskDetails/pds.css @@ -1,4 +1,4 @@ -.ysldivhome2{ +.ysldivhome2s{ display: flex; flex-flow: row wrap; align-content:stretch; @@ -6,20 +6,17 @@ margin-bottom: 20px; } -.ysllogin_register_contents{ - display: flex; - margin-top: 20px; - /*justify-content: center;*/ - background: #fff; -} - .ysllogin_register_contentss{ margin-top:0px !important; padding-top: 10px; padding-bottom: 10px; + display: flex; + margin-top: 20px; + /*justify-content: center;*/ + background: #fff; } -.ysldivhomediv1{ +.ysldivhomediv1s{ width: 80px; height: 130px; display: flex; @@ -33,10 +30,10 @@ margin-left: 64px; } -.yslgouxuanimg2{ +.yslgouxuanimg2s{ height: 20px; } -.div1img{ +.div1imgs{ display: flex; justify-content:center; width: 80px; @@ -53,7 +50,7 @@ .ptext{ width: 80px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } -.ysllogin_section { +.ysllogin_sections { display: flex; align-items: center; flex-direction: column; diff --git a/public/react/src/modules/projectPackages/PackageIndexNewandEdit/PackageIndexNEIBannerConcent.js b/public/react/src/modules/projectPackages/PackageIndexNewandEdit/PackageIndexNEIBannerConcent.js index e7e4b20a2..eae5589f2 100644 --- a/public/react/src/modules/projectPackages/PackageIndexNewandEdit/PackageIndexNEIBannerConcent.js +++ b/public/react/src/modules/projectPackages/PackageIndexNewandEdit/PackageIndexNEIBannerConcent.js @@ -66,13 +66,7 @@ class PackageIndexNEIBannerConcent extends Component { categories:[] } } - componentDidUpdate = (prevProps) => { - if(prevProps.current_user.username!=this.props.current_user.username){ - this.setState({ - contact_name:this.props.current_user.username - }) - } - } + componentDidMount() { window.document.title = '众包创新' @@ -99,9 +93,7 @@ class PackageIndexNEIBannerConcent extends Component { }) }else{ - this.setState({ - contact_name:this.props.current_user.username - }) + console.log(this.props.current_user&&this.props.current_user.username) } let Url = `/project_package_categories.json`; @@ -119,8 +111,24 @@ class PackageIndexNEIBannerConcent extends Component { console.log(error) }) + this.setState({ + contact_name:this.props.current_user&&this.props.current_user.username + }) + // 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 =()=>{ // 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===""){ this.setState({ contact_phonetype:true @@ -445,7 +453,7 @@ class PackageIndexNEIBannerConcent extends Component { min_price:parseInt(min_price), max_price:parseInt(max_price), 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, publish:types } @@ -454,7 +462,7 @@ class PackageIndexNEIBannerConcent extends Component { if(type===true){ this.props.setPublicationfun(response.data.id) }else{ - window.location.href="/crowdsourcing/"+response.data.id + window.location.href="/crowdsourcings/"+response.data.id } this.setState({ springtype:false @@ -493,7 +501,7 @@ class PackageIndexNEIBannerConcent extends Component { min_price:parseInt(min_price), max_price:parseInt(max_price), 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, publish:types } @@ -502,7 +510,7 @@ class PackageIndexNEIBannerConcent extends Component { if(type===true){ this.props.setPublicationfun(response.data.id) }else{ - window.location.href="/crowdsourcing/"+response.data.id + window.location.href="/crowdsourcings/"+response.data.id } this.setState({ 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===""){ this.setState({ contact_phonetype:true @@ -707,9 +715,9 @@ class PackageIndexNEIBannerConcent extends Component {

    -
    +
    - +

    @@ -724,7 +732,7 @@ class PackageIndexNEIBannerConcent extends Component { {this.state.categorytypes===true?

    请选择类型
    :""}
    - +
    - +

    竞标截止: @@ -833,35 +841,35 @@ class PackageIndexNEIBannerConcent extends Component { {this.state.minmaxtype===true?

    最高费用不能小于最低费用
    :""}

    - +

    - 姓名: + 姓名: this.onChangeContact_name(e)} /> {this.state.contact_nametype===true?

    不能为空
    :""}

    - {modalCancel===false&&this.props.current_user.phone!=null?

    + {this.props.current_user&&this.props.current_user.phone!=null&&modalCancel===false?

    手机号: - + this.editmodels()}>

    :""} {/*{this.state.current_userphonetype===true?
    不能为空
    :""}*/} - {modalCancel===true||this.props.current_user.phone===null?

    + {this.props.current_user&&this.props.current_user.phone===null||modalCancel===true?

    {/*未注册才显示!*/} @@ -897,7 +905,7 @@ class PackageIndexNEIBannerConcent extends Component { {/**/} - + this.modalCancel()}>X

    :""} diff --git a/public/react/src/modules/projectPackages/PackageIndexNewandEdit/PackageIndexNEISubmit.js b/public/react/src/modules/projectPackages/PackageIndexNewandEdit/PackageIndexNEISubmit.js index 9f287e90e..932b9295f 100644 --- a/public/react/src/modules/projectPackages/PackageIndexNewandEdit/PackageIndexNEISubmit.js +++ b/public/react/src/modules/projectPackages/PackageIndexNewandEdit/PackageIndexNEISubmit.js @@ -14,10 +14,10 @@ class PackageIndexNEISubmit extends Component { } setageload=(sum)=>{ if(sum===undefined){ - window.location.href="/crowdsourcing/new" + window.location.href="/crowdsourcings/new" }else{ // 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 {

    -
    恭喜!
    -
    提交成功
    +
    恭喜!
    +
    提交成功
    平台正在审核您的申请,审核结果将以平台消息的形式通知您
    this.setageload(this.props.id)}>查看发布需求 diff --git a/public/react/src/modules/projectPackages/PackageIndexNewandEdit/PackageIndexNewandEditIndex.js b/public/react/src/modules/projectPackages/PackageIndexNewandEdit/PackageIndexNewandEditIndex.js index 1571fd77a..8d73a6e55 100644 --- a/public/react/src/modules/projectPackages/PackageIndexNewandEdit/PackageIndexNewandEditIndex.js +++ b/public/react/src/modules/projectPackages/PackageIndexNewandEdit/PackageIndexNewandEditIndex.js @@ -27,6 +27,12 @@ class PackageIndexNewandEditIndex extends Component{ id:ids }) } + + goback = () => { + // window.history.go(-1) + window.location.href="/crowdsourcings"; + } + render() { let {setPublication}=this.state; return ( @@ -37,6 +43,7 @@ class PackageIndexNewandEditIndex extends Component{

    {this.props.match.params.id!=undefined?"编辑":"新建"} + 返回

    diff --git a/public/react/src/modules/projectPackages/packageconcnet.css b/public/react/src/modules/projectPackages/packageconcnet.css index 498da9214..7a8c11897 100644 --- a/public/react/src/modules/projectPackages/packageconcnet.css +++ b/public/react/src/modules/projectPackages/packageconcnet.css @@ -167,7 +167,7 @@ color:#999; } .padding251{ - padding: 0px 251px; + padding: 0px 245px; } .ant-modal-title{ @@ -344,3 +344,17 @@ 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; +} \ No newline at end of file diff --git a/public/react/src/modules/tpm/NewHeader.js b/public/react/src/modules/tpm/NewHeader.js index 379c70e27..b84982d86 100644 --- a/public/react/src/modules/tpm/NewHeader.js +++ b/public/react/src/modules/tpm/NewHeader.js @@ -718,7 +718,7 @@ submittojoinclass=(value)=>{
  • 教学案例
  • 众包创新
  • 交流问答
  • {
  • 我的实训
  • 我的实践课程
  • 我的项目
  • + {/*
  • 我的众包
  • */}
  • 账号管理
  • {/*
  • this.educoderlogin()} >登入测试接口
  • */} {/*
  • this.trialapplications()} >试用申请
  • */} diff --git a/public/react/src/modules/tpm/TPMIndexHOC.js b/public/react/src/modules/tpm/TPMIndexHOC.js index 0cf22376f..b0bcf6f2a 100644 --- a/public/react/src/modules/tpm/TPMIndexHOC.js +++ b/public/react/src/modules/tpm/TPMIndexHOC.js @@ -20,7 +20,7 @@ const versionNum = '0001'; // let _url_origin = getUrl() let _url_origin=''; 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`; @@ -32,14 +32,14 @@ if (!window['indexHOCLoaded']) { // $('head').append($('') // .attr('href', `${_url_origin}/stylesheets/educoder/antd.min.css?1525440977`)); $('head').append($('') - .attr('href', `${_url_origin}/stylesheets/css/edu-common.css?1525440977`)); + .attr('href', `${_url_origin}/stylesheets/css/edu-common.css?15254409771`)); $('head').append($('') - .attr('href', `${_url_origin}/stylesheets/educoder/edu-main.css?1525440978`)); + .attr('href', `${_url_origin}/stylesheets/educoder/edu-main.css?15254409781`)); // index.html有加载 $('head').append($('') - .attr('href', `${_url_origin}/stylesheets/educoder/edu-all.css?1525440978`)); + .attr('href', `${_url_origin}/stylesheets/educoder/edu-all.css?15254409781`)); // $('head').append($('') diff --git a/public/react/src/modules/user/account/AccountBasicEdit.js b/public/react/src/modules/user/account/AccountBasicEdit.js index a4637c00e..612e47d9f 100644 --- a/public/react/src/modules/user/account/AccountBasicEdit.js +++ b/public/react/src/modules/user/account/AccountBasicEdit.js @@ -719,7 +719,7 @@ class AccountBasic extends Component { { - (!filterDepartments || (filterDepartments && filterDepartments.length==0 ) + filterDepartments != undefined && ( (filterDepartments && filterDepartments.length==0 ) || (departmentsName == '' && !this.state.department_id && (!departments || departments.length == 0) )) &&
    diff --git a/public/react/src/modules/user/modal/ApplyForAddChildOrgModal.js b/public/react/src/modules/user/modal/ApplyForAddChildOrgModal.js index be53dbdec..db32beed3 100644 --- a/public/react/src/modules/user/modal/ApplyForAddChildOrgModal.js +++ b/public/react/src/modules/user/modal/ApplyForAddChildOrgModal.js @@ -16,7 +16,7 @@ class ApplyForAddChildOrgModal extends Component{ } componentDidUpdate=(prevState)=>{ - if(this.props.departmentName && prevState.departmentName != this.props.departmentName){ + if(prevState.departmentName != this.props.departmentName){ this.setValue(this.props.departmentName) } } diff --git a/public/react/src/modules/user/usersInfo/Infos.js b/public/react/src/modules/user/usersInfo/Infos.js index 3b58681da..f20cd5be5 100644 --- a/public/react/src/modules/user/usersInfo/Infos.js +++ b/public/react/src/modules/user/usersInfo/Infos.js @@ -17,6 +17,12 @@ import "../../courses/css/Courses.css" import Trialapplication from '../../login/Trialapplication' + +const InfosPackage = Loadable({ + loader: () => import('./InfosPackage'), + loading:Loading, +}) + const InfosCourse = Loadable({ loader: () => import('./InfosCourse'), loading:Loading, @@ -373,11 +379,17 @@ class Infos extends Component{ to={`/users/${username}/paths`}>实践课程
  • - this.setState({moduleName: 'projects'})} to={`/users/${username}/projects`}>项目
  • - + + {/*
  • */} + {/* this.setState({moduleName: 'package'})}*/} + {/*to={`/users/${username}/package`}>众包*/} + {/*
  • */} + {/*{ data && data.identity!="学生" &&
  • 题库
  • }*/}
    @@ -389,6 +401,15 @@ class Infos extends Component{ {/* --------------------------------------------------------------------- */} + + {/* 众包 */} + {/* http://localhost:3007/courses/1309/homework/9300/setting */} + () + } + > + {/* 课堂 */} {/* http://localhost:3007/courses/1309/homework/9300/setting */} () } > + + () diff --git a/public/react/src/modules/user/usersInfo/InfosPackage.js b/public/react/src/modules/user/usersInfo/InfosPackage.js new file mode 100644 index 000000000..f1077cd62 --- /dev/null +++ b/public/react/src/modules/user/usersInfo/InfosPackage.js @@ -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( + + ) + } +} +export default InfosPackage; \ No newline at end of file diff --git a/public/stylesheets/educoder/edu-all.css b/public/stylesheets/educoder/edu-all.css index 65ff14a59..ec245b196 100644 --- a/public/stylesheets/educoder/edu-all.css +++ b/public/stylesheets/educoder/edu-all.css @@ -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%;} .path-nav li{float: left;padding: 0px 30px;height: 42px;} .path-nav li a{color:#fff;font-size: 16px;display: block; height: 40px;} diff --git a/spec/controllers/forums_controller_spec.rb b/spec/controllers/forums_controller_spec.rb new file mode 100644 index 000000000..a4143d232 --- /dev/null +++ b/spec/controllers/forums_controller_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe ForumsController, type: :controller do + +end diff --git a/spec/helpers/forums_helper_spec.rb b/spec/helpers/forums_helper_spec.rb new file mode 100644 index 000000000..46766dbff --- /dev/null +++ b/spec/helpers/forums_helper_spec.rb @@ -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 diff --git a/spec/models/forum_spec.rb b/spec/models/forum_spec.rb new file mode 100644 index 000000000..56c246346 --- /dev/null +++ b/spec/models/forum_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Forum, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end