diff --git a/.gitignore b/.gitignore index 349c0cdc1..544539736 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ # Ignore bundler config. /.bundle /bundle +/node_modules # Ignore lock config file *.lock @@ -46,6 +47,8 @@ /public/h5build /public/npm-debug.log +/dist + # avatars /public/images/avatars diff --git a/app/controllers/attendances_controller.rb b/app/controllers/attendances_controller.rb index dc30df7e3..c8287f413 100644 --- a/app/controllers/attendances_controller.rb +++ b/app/controllers/attendances_controller.rb @@ -8,23 +8,38 @@ class AttendancesController < ApplicationController current_date = Date.current current_end_time = Time.current.strftime("%H:%M:%S") + member = @course.students.find_by(user_id: current_user.id) + if params[:history] - @attendances = @course.course_attendances.where("attendance_date < '#{current_date}' or - (attendance_date = '#{current_date}' and end_time < '#{current_end_time}')") if @user_course_identity == Course::STUDENT - member = @course.students.find_by(user_id: current_user.id) - group_ids = [member&.course_group_id.to_i, 0] - @attendances = @attendances.joins(:course_attendance_groups).where(course_attendance_groups: {course_group_id: group_ids}) + history_attendance_ids = member.course_member_attendances.where(course_id: @course.id).pluck(:course_attendance_id) + @attendances = @course.course_attendances.where(id: history_attendance_ids.uniq). + where("attendance_date < '#{current_date}' or (attendance_date = '#{current_date}' and end_time < '#{current_end_time}')") + else + @attendances = @course.course_attendances.where("attendance_date < '#{current_date}' or + (attendance_date = '#{current_date}' and end_time < '#{current_end_time}')") + end + else + @attendances = @course.course_attendances.where("attendance_date > '#{current_date}' or + (attendance_date = '#{current_date}' and end_time > '#{current_end_time}')") + end + + if @user_course_identity == Course::STUDENT + group_ids = [member&.course_group_id.to_i, 0] + @attendances = @attendances.joins(:course_attendance_groups).where(course_attendance_groups: {course_group_id: group_ids}) + + if params[:history] attendance_ids = @attendances.pluck(:id) @normal_count = @course.course_member_attendances.where(course_member_id: member&.id, course_attendance_id: attendance_ids, attendance_status: "NORMAL").size @leave_count = @course.course_member_attendances.where(course_member_id: member&.id, course_attendance_id: attendance_ids, attendance_status: "LEAVE").size @absence_count = @course.course_member_attendances.where(course_member_id: member&.id, course_attendance_id: attendance_ids, attendance_status: "ABSENCE").size + else + @attendances = @attendances.where("attendance_date = '#{current_date}' and start_time <= '#{current_end_time}' and end_time > '#{current_end_time}'") end - else - @attendances = @course.course_attendances.where("attendance_date > '#{current_date}' or - (attendance_date = '#{current_date}' and end_time > '#{current_end_time}')") + end + @attendances_count = @attendances.size @attendances = @attendances.order("attendance_date desc, start_time desc") diff --git a/app/controllers/colleges_controller.rb b/app/controllers/colleges_controller.rb index 53fbfe323..9d1eb941e 100644 --- a/app/controllers/colleges_controller.rb +++ b/app/controllers/colleges_controller.rb @@ -27,7 +27,7 @@ class CollegesController < ApplicationController def shixun_time time_sum = Game.joins('left join user_extensions on user_extensions.user_id = games.user_id') .where(user_extensions: { school_id: current_school.id }).sum(:cost_time) - shixun_time_sum = (time_sum / (24 * 60 * 60.0)).ceil + shixun_time_sum = (time_sum / 60.0).ceil render json: { shixun_time: shixun_time_sum } end diff --git a/app/controllers/course_modules_controller.rb b/app/controllers/course_modules_controller.rb index bea248b52..821ee81a5 100644 --- a/app/controllers/course_modules_controller.rb +++ b/app/controllers/course_modules_controller.rb @@ -50,11 +50,16 @@ class CourseModulesController < ApplicationController # 添加二级目录 def add_second_category tip_exception("子目录名称不能为空") if params[:name].blank? - tip_exception("已存在同名子目录") if @course_module.course_second_categories.exists?(name: params[:name].strip) + parent_id = params[:parent_id].to_i + if parent_id != 0 + parent_node = @course_module.course_second_categories.find_by(id: parent_id) + tip_exception("上级目录不存在") if parent_node.blank? + end + tip_exception("已存在同名子目录") if @course_module.course_second_categories.exists?(parent_id: parent_id, name: params[:name].strip) ActiveRecord::Base.transaction do begin category = @course_module.course_second_categories.create!(name: params[:name].strip, category_type: @course_module.module_type, - course_id: @course.id, position: @course_module.course_second_categories.count + 1) + course_id: @course.id, position: @course_module.course_second_categories.where(parent_id: parent_id).count + 1, parent_id: parent_id) render :json => {category_id: category.id, status: 0, message: "添加成功"} rescue Exception => e uid_logger_error(e.message) diff --git a/app/controllers/course_second_categories_controller.rb b/app/controllers/course_second_categories_controller.rb index c59ffbdbe..45d3804d3 100644 --- a/app/controllers/course_second_categories_controller.rb +++ b/app/controllers/course_second_categories_controller.rb @@ -7,7 +7,7 @@ class CourseSecondCategoriesController < ApplicationController def rename_category tip_exception("毕设子目录不能重命名") if @category.category_type == "graduation" tip_exception("名称不能为空") if params[:name].blank? - tip_exception("已存在同名子目录") if @course_module.course_second_categories.exists?(name: params[:name].strip) + tip_exception("已存在同名子目录") if @course_module.course_second_categories.exists?(parent_id: @category.parent_id, name: params[:name].strip) @category.update_attributes!(name: params[:name].strip) normal_status(0, "更新成功") end @@ -17,9 +17,13 @@ class CourseSecondCategoriesController < ApplicationController tip_exception("移动失败") if params[:position].blank? unless params[:position].to_i == @category.position if params[:position].to_i < @category.position - @course_module.course_second_categories.where("position < #{@category.position} and position >= ?", params[:position]).update_all("position = position + 1") + @course_module.course_second_categories + .where("parent_id = #{@category.parent_id} and position < #{@category.position} and position >= ?", params[:position]) + .update_all("position = position + 1") else - @course_module.course_second_categories.where("position > #{@category.position} and position <= ?", params[:position]).update_all("position = position - 1") + @course_module.course_second_categories + .where("parent_id = #{@category.parent_id} and position > #{@category.position} and position <= ?", params[:position]) + .update_all("position = position - 1") end @category.update!(position: params[:position]) normal_status(0, "移动成功") @@ -32,22 +36,30 @@ class CourseSecondCategoriesController < ApplicationController tip_exception("毕设子目录不能删除") if @category.category_type == "graduation" ActiveRecord::Base.transaction do begin - @course_module.course_second_categories.where("position > #{@category.position}").update_all("position = position - 1") + parent_id = @category.parent_id + @course_module.course_second_categories.where("parent_id = #{parent_id} and position > #{@category.position}") + .update_all("position = position - 1") + + # 更新相应对象的子目录id if @course_module.module_type == "shixun_homework" - @category.homework_commons.update_all(course_second_category_id: 0) + @category.homework_commons.update_all(course_second_category_id: parent_id) @right_url = "/classrooms/#{@course.id}/shixun_homeworks/#{@course_module.id}" elsif @course_module.module_type == "attachment" - Attachment.where(course_second_category_id: @category.id).update_all(course_second_category_id: 0) - @right_url = "/classrooms/#{@course.id}/files/#{@course_module.id}" + Attachment.where(course_second_category_id: @category.id).update_all(course_second_category_id: parent_id) + if parent_id == 0 + @right_url = "/classrooms/#{@course.id}/files/#{@course_module.id}" + else + @right_url = "/classrooms/#{@course.id}/file/#{parent_id}" + end elsif @course_module.module_type == "video" - @course.course_videos.where(course_second_category_id: @category.id).update_all(course_second_category_id: 0) + @course.course_videos.where(course_second_category_id: @category.id).update_all(course_second_category_id: parent_id) @right_url = "/classrooms/#{@course.id}/course_videos" elsif @course_module.module_type == "common_homework" - @category.homework_commons.update_all(course_second_category_id: 0) + @category.homework_commons.update_all(course_second_category_id: parent_id) @right_url = "/classrooms/#{@course.id}/common_homeworks/#{@course_module.id}" elsif @course_module.module_type == "group_homework" - @category.homework_commons.update_all(course_second_category_id: 0) + @category.homework_commons.update_all(course_second_category_id: parent_id) @right_url = "/classrooms/#{@course.id}/group_homeworks/#{@course_module.id}" end diff --git a/app/controllers/course_videos_controller.rb b/app/controllers/course_videos_controller.rb index 090c7e29a..c5e0358cd 100644 --- a/app/controllers/course_videos_controller.rb +++ b/app/controllers/course_videos_controller.rb @@ -1,9 +1,9 @@ class CourseVideosController < ApplicationController before_action :require_login - before_action :validate_params + before_action :validate_params, except: [:watch_histories] before_action :find_course, only: [:create] - before_action :find_video, only: [:update] - before_action :teacher_allowed + before_action :find_video, only: [:update, :watch_histories] + before_action :teacher_allowed, except: [:watch_histories] def create title = params[:name].strip @@ -20,6 +20,33 @@ class CourseVideosController < ApplicationController render_ok end + def watch_histories + return normal_status(403, "你没有权限操作") if !current_user.teacher_of_course?(@course) + course_video = CourseVideo.find(@video.id) + + @watch_course_videos = course_video.watch_course_videos.joins(" + JOIN watch_video_histories ON watch_video_histories.watch_course_video_id = watch_course_videos.id + ").group("watch_video_histories.watch_course_video_id").where("watch_course_videos.end_at IS NOT NULL").select("watch_course_videos.id") + + @count = @watch_course_videos.count.count + + if params[:group_id].present? + @watch_course_videos = @watch_course_videos.joins(" + JOIN course_members ON course_members.user_id = watch_course_videos.user_id AND course_members.course_id = #{@course.id} + ").where("course_members.course_group_id = ?", params[:group_id]) + end + + @watch_course_videos = @watch_course_videos.select("count(watch_video_histories.id) AS freq, watch_course_videos.*") + + if params[:order].present? + key = params[:order].split("-") + if ["freq", 'total_duration'].include?(key.first) && ["desc", "asc"].include?(key.last) + @watch_course_videos = @watch_course_videos.order("#{key.first} #{key.last}") + end + end + @watch_course_videos = paginate @watch_course_videos + end + private def validate_params diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb index 68211d034..bdb5daae1 100644 --- a/app/controllers/courses_controller.rb +++ b/app/controllers/courses_controller.rb @@ -30,7 +30,8 @@ class CoursesController < ApplicationController :informs, :update_informs, :online_learning, :update_task_position, :tasks_list, :join_excellent_course, :export_couser_info, :export_member_act_score, :new_informs, :delete_informs, :change_member_role, :course_groups, :join_course_group, :statistics, - :work_score, :act_score, :calculate_all_shixun_scores, :move_to_category] + :work_score, :act_score, :calculate_all_shixun_scores, :move_to_category, :watch_video_histories, :watch_statics, + :own_watch_histories] before_action :user_course_identity, except: [:join_excellent_course, :index, :create, :new, :apply_to_join_course, :search_course_list, :get_historical_course_students, :mine, :search_slim, :board_list] before_action :teacher_allowed, only: [:update, :destroy, :settings, :search_teacher_candidate, @@ -114,11 +115,12 @@ class CoursesController < ApplicationController #sql = "left join videos on videos.id=course_videos.video_id AND (videos.transcoded=1 OR videos.user_id = #{current_user.id})" #@videos = paginate videos.joins(sql).includes(video: [user: :user_extension], user: :user_extension) - videos = videos.includes(video: [user: :user_extension], user: :user_extension) + videos = videos.includes(video: [user: :user_extension],user: :user_extension).select("course_videos.id, course_videos.title, course_videos.link, course_videos.user_id") videos = videos.where(videos: {transcoded: true}) .or(videos.where(videos: {user_id: current_user.id})) .or(videos.where(course_videos: {is_link: true})) - @count = videos.count + @count = videos.count("course_videos.id") + logger.info("#######count:#{@count}") @videos = paginate videos end @@ -1297,7 +1299,7 @@ class CoursesController < ApplicationController def left_banner @user = current_user @is_teacher = @user_course_identity < Course::ASSISTANT_PROFESSOR - @course_modules = @course.course_modules.where(hidden: 0) + @course_modules = @course.course_modules.where(hidden: 0).includes(first_categories: :children) @hidden_modules = @course.course_modules.where(hidden: 1) @second_category_type = ["shixun_homework", "graduation", "attachment", "board", "course_group", "video", "common_homework", "group_homework"] end @@ -1318,6 +1320,7 @@ class CoursesController < ApplicationController def attahcment_category_list @has_course_groups = @course.course_groups.exists? @course_modules = @course.attachment_course_modules + @no_child = params[:no_child] end def mine @@ -1481,6 +1484,76 @@ class CoursesController < ApplicationController end end + # 课堂视频观看记录总览 + def watch_video_histories + return normal_status(403, "你没有权限操作") unless current_user.teacher_of_course?(@course) + + @videos = CourseVideo.joins(" + JOIN videos ON course_videos.course_id = #{@course.id} AND videos.id = course_videos.video_id + LEFT JOIN ( + SELECT watch_course_videos.course_video_id, SUM(watch_course_videos.total_duration) AS time, COUNT(watch_course_videos.course_video_id) AS num + FROM watch_course_videos + JOIN course_videos ON course_videos.id = watch_course_videos.course_video_id AND watch_course_videos.end_at IS NOT NULL + WHERE course_videos.course_id = #{@course.id} + GROUP BY watch_course_videos.course_video_id + ) AS hisotries ON hisotries.course_video_id = course_videos.id").select("course_videos.id") + + @count = @videos.count + + if params[:order].present? + key = params[:order].split("-") + if ["people_num", 'total_time'].include?(key.first) && ["desc", "asc"].include?(key.last) + @videos = @videos.order("#{key.first} #{key.last}") + end + end + + @videos = @videos.select("course_videos.id, videos.user_id, videos.title, IFNULL(hisotries.time,0) AS total_time, IFNULL(hisotries.num,0) AS people_num") + + @videos = paginate @videos + end + + # 学生角度观看课堂视频的记录 + def own_watch_histories + @current_user = current_user + + @videos = CourseVideo.joins(" + JOIN videos ON course_videos.course_id = #{@course.id} AND videos.id = course_videos.video_id + JOIN watch_course_videos ON course_videos.id = watch_course_videos.course_video_id AND watch_course_videos.user_id = #{current_user.id} + JOIN ( + SELECT watch_course_videos.course_video_id, COUNT(watch_course_videos.course_video_id) AS num + FROM watch_course_videos + JOIN course_videos ON course_videos.id = watch_course_videos.course_video_id + WHERE course_videos.course_id = #{@course.id} AND watch_course_videos.user_id = #{current_user.id} AND watch_course_videos.end_at IS NOT NULL + GROUP BY watch_course_videos.course_video_id + ) AS hisotries ON hisotries.course_video_id = course_videos.id").select("course_videos.id") + + @count = @videos.count + + if params[:order].present? + key = params[:order].split("-") + if ["freq", 'total_duration'].include?(key.first) && ["desc", "asc"].include?(key.last) + @videos = @videos.order("#{key.first} #{key.last}") + end + end + + @videos = @videos.select("course_videos.id, watch_course_videos.start_at, watch_course_videos.total_duration, watch_course_videos.end_at, watch_course_videos.is_finished, videos.title, IFNULL(hisotries.num,0) AS freq") + + @videos = paginate @videos + end + + # 课堂视频的统计总览 + def watch_statics + @total_duration = @course.course_videos.joins(:watch_course_videos).sum(:total_duration).round(2) + @frequencies = @course.course_videos.joins([watch_course_videos: :watch_video_histories]).count(:id) + @people_num = @course.course_videos.joins(:watch_course_videos).count(:id) + render json: { + total_duration: @total_duration, + freq: @frequencies, + people_num: @people_num, + begin_at: '2020-03-13 24:00' + } + end + private # Use callbacks to share common setup or constraints between actions. diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb index 03e6dad24..8e6356201 100644 --- a/app/controllers/files_controller.rb +++ b/app/controllers/files_controller.rb @@ -18,11 +18,23 @@ class FilesController < ApplicationController sort_type = params[:sort_type] || 'created_on' # created_on:时间排序, downloads:下载次数排序; quotes: 引用次数排序 @course_second_category_id = params[:course_second_category_id] || 0 # 0: 为主目录, 其他为次目录id @user = current_user - @attachments = @course_second_category_id.to_i == 0 ? @course.attachments.includes(:course_second_category) : @course.attachments.by_course_second_category_id(@course_second_category_id) + get_category(@course, @course_second_category_id) + + # 主目录显示所有资源,一级目录显示一级和所属二级目录的资源,二级目录只显示该目录下的资源 + if @course_second_category_id.to_i == 0 + @attachments = @course.attachments.includes(:course_second_category) + else + if @parent_category_id == 0 + category_ids = [@category_id] + @category.children.pluck(:id) + @attachments = @course.attachments.where(course_second_category_id: category_ids) + else + @attachments = @course.attachments.by_course_second_category_id(@course_second_category_id) + end + end + @attachments = @attachments.includes(author: [:user_extension, :course_members]) .ordered(sort: sort.to_i, sort_type: sort_type.strip) - get_category(@course, @course_second_category_id) @total_count = @attachments.size @unlink_count = @attachments.no_link.size @@ -354,9 +366,10 @@ class FilesController < ApplicationController @category_id = category.try(:id) @category_name = category.try(:module_name) else - category = CourseSecondCategory.find category_id - @category_id = category.try(:id) - @category_name = category.try(:name) + @category = CourseSecondCategory.find category_id + @category_id = @category.try(:id) + @category_name = @category.try(:name) + @parent_category_id = @category&.parent_id.to_i end end diff --git a/app/controllers/homework_commons_controller.rb b/app/controllers/homework_commons_controller.rb index faea30242..f88217f7b 100644 --- a/app/controllers/homework_commons_controller.rb +++ b/app/controllers/homework_commons_controller.rb @@ -937,8 +937,14 @@ class HomeworkCommonsController < ApplicationController publish_groups = charge_group_ids & group_ids if group_ids # ActiveRecord::Base.transaction do + messages = [] homeworks.each do |homework| # 作业未发布时 + max_end_time = params[:detail] ? group_end_times.max : params[:end_time] + if homework.allow_late && max_end_time.to_time >= homework.late_time + messages << "#{homework.name}" + next + end if homework.homework_detail_manual.try(:comment_status) == 0 if !params[:group_ids].blank? @@ -1018,7 +1024,14 @@ class HomeworkCommonsController < ApplicationController # 更新学生状态及成绩(手动点击计算) # HomeworkPublishUpdateWorkStatusJob.perform_later(tiding_group_ids, homework.id) end - normal_status(0, "发布成功") + unless messages.blank? + return_message = messages.join("、") + return_message += messages.size == 1 ? "" : "等" + return_message += "作业发布失败,原因:截止时间必须早于补交结束时间" + else + return_message = "发布成功" + end + normal_status(0, return_message) # end end diff --git a/app/controllers/memos_controller.rb b/app/controllers/memos_controller.rb index c0ea8d601..ef61ea202 100644 --- a/app/controllers/memos_controller.rb +++ b/app/controllers/memos_controller.rb @@ -12,7 +12,14 @@ class MemosController < ApplicationController def index @user = current_user @memos = Memo.all - s_order = (params[:order] == "replies_count" ? "all_replies_count" : params[:order]) || "updated_at" + # replies_count created_at updated_at + s_order = + case params[:order] + when 'replies_count' then 'all_replies_count' + when 'created_at' then 'created_at' + else + 'updated_at' + end # @tidding_count = unviewed_tiddings(current_user) if current_user.present? page = params[:page] || 1 limit = params[:limit] || 15 diff --git a/app/controllers/shixuns_controller.rb b/app/controllers/shixuns_controller.rb index 3f202c994..b1920239c 100644 --- a/app/controllers/shixuns_controller.rb +++ b/app/controllers/shixuns_controller.rb @@ -70,7 +70,7 @@ class ShixunsController < ApplicationController end ## 排序参数 - bsort = params[:sort] || 'desc' + bsort = (params[:sort] == "desc" ? "desc" : "asc") case params[:order_by] || 'new' when 'hot' @shixuns = @shixuns.order("shixuns.public = 2 desc, shixuns.myshixuns_count #{bsort}") diff --git a/app/controllers/watch_video_histories_controller.rb b/app/controllers/watch_video_histories_controller.rb index 15ee62113..fe1fbf31b 100644 --- a/app/controllers/watch_video_histories_controller.rb +++ b/app/controllers/watch_video_histories_controller.rb @@ -5,6 +5,6 @@ class WatchVideoHistoriesController < ApplicationController watch_log = CreateWatchVideoService.new(current_user, request, params).call render_ok(log_id: watch_log&.id) rescue CreateWatchVideoService::Error => ex - render_error(ex.message) + render_ok(log_id: watch_log&.id) end end diff --git a/app/controllers/weapps/attendances_controller.rb b/app/controllers/weapps/attendances_controller.rb index da22aa300..90f8c24b6 100644 --- a/app/controllers/weapps/attendances_controller.rb +++ b/app/controllers/weapps/attendances_controller.rb @@ -9,8 +9,10 @@ class Weapps::AttendancesController < ApplicationController def create ActiveRecord::Base.transaction do attendance = @course.course_attendances.create!(create_params.merge(user_id: current_user.id)) - unless params[:group_ids].blank? || @course.course_groups.where(id: params[:group_ids]).count == @course.course_groups.count - group_ids = @course.charge_group_ids(current_user) & params[:group_ids].map(&:to_i) + group_ids = params[:group_ids] || [] + group_ids = group_ids.blank? ? @course.charge_group_ids(current_user) : @course.charge_group_ids(current_user) & params[:group_ids].map(&:to_i) + unless group_ids.blank? || @course.course_groups.where(id: group_ids).count == @course.course_groups.count + # group_ids = @course.charge_group_ids(current_user) & params[:group_ids].map(&:to_i) group_ids.each do |group_id| @course.course_attendance_groups.create!(course_group_id: group_id, course_attendance: attendance) end @@ -114,6 +116,8 @@ class Weapps::AttendancesController < ApplicationController @course_members = @course.students if @_is_current_attendance @all_attendances = @attendance.course_member_attendances + + @user = @attendance.user end def update diff --git a/app/controllers/weapps/course_member_attendances_controller.rb b/app/controllers/weapps/course_member_attendances_controller.rb index 5e92d5928..254a89b1d 100644 --- a/app/controllers/weapps/course_member_attendances_controller.rb +++ b/app/controllers/weapps/course_member_attendances_controller.rb @@ -14,6 +14,9 @@ class Weapps::CourseMemberAttendancesController < ApplicationController @members = @members.where(course_group_id: params[:group_ids]) end + @page = params[:page] || 1 + @limit = params[:limit] || 5 + if params[:attendance_status].present? @members = @members.joins(:course_member_attendances).where(course_member_attendances: {course_attendance_id: attendance.id, attendance_status: params[:attendance_status]}) end @@ -34,7 +37,7 @@ class Weapps::CourseMemberAttendancesController < ApplicationController if params[:attendance_mode] == "QUICK" attendance = CourseAttendance.find_by(id: params[:attendance_id]) else - attendance = CourseAttendance.find_by(attendance_code: params[:code]) + attendance = CourseAttendance.find_by(id: params[:attendance_id], attendance_code: params[:code]) end tip_exception("该签到不存在") if attendance.blank? || attendance.course.blank? diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index d1d2499f0..88ed373d9 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -140,11 +140,11 @@ module ApplicationHelper # 用户图像url,如果不存在的话,source为匿名用户,即默认使用匿名用户图像 def url_to_avatar(source) if File.exist?(disk_filename(source&.class, source&.id)) - ctime = File.ctime(disk_filename(source.class, source.id)).to_i + ctime = File.ctime(disk_filename(source&.class, source&.id)).to_i if source.class.to_s == 'User' - File.join(relative_path, ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}" + File.join(relative_path, ["#{source&.class}", "#{source&.id}"]) + "?t=#{ctime}" else - File.join("images/avatars", ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}" + File.join("images/avatars", ["#{source&.class}", "#{source&.id}"]) + "?t=#{ctime}" end elsif source.class.to_s == 'User' str = source.user_extension.try(:gender).to_i == 0 ? "b" : "g" diff --git a/app/helpers/courses_helper.rb b/app/helpers/courses_helper.rb index 27444b3c9..4e088c0fd 100644 --- a/app/helpers/courses_helper.rb +++ b/app/helpers/courses_helper.rb @@ -81,6 +81,8 @@ module CoursesHelper "/classrooms/#{course.id}/statistics" when "video" "/classrooms/#{course.id}/course_videos" + when "attendance" + "/classrooms/#{course.id}/attendances" end end @@ -131,31 +133,33 @@ module CoursesHelper # 课堂模块的任务数 def course_task_count(course, module_type) case module_type - when "shixun_homework" - get_homework_commons_count(course, 4, 0) - when "common_homework" - get_homework_commons_count(course, 1, 0) - when "group_homework" - get_homework_commons_count(course, 3, 0) - when "graduation" - 0 - when "exercise" - course.exercises_count - when "poll" - course.polls_count - when "attachment" - get_attachment_count(course, 0) - when "board" - course_board = course.course_board - course_board.present? ? course_board.messages.size : 0 - when "course_group" - course.course_groups_count - when "announcement" - course.informs.count - when "online_learning" - course.shixuns.count - when "video" - course.videos_count + course.live_links.count + when "shixun_homework" + get_homework_commons_count(course, 4, 0) + when "common_homework" + get_homework_commons_count(course, 1, 0) + when "group_homework" + get_homework_commons_count(course, 3, 0) + when "graduation" + 0 + when "exercise" + course.exercises_count + when "poll" + course.polls_count + when "attachment" + get_attachment_count(course, 0) + when "board" + course_board = course.course_board + course_board.present? ? course_board.messages.size : 0 + when "course_group" + course.course_groups_count + when "announcement" + course.informs.count + when "online_learning" + course.shixuns.count + when "video" + course.videos_count + course.live_links.count + when "attendance" + course.course_attendances.count end end diff --git a/app/jobs/student_join_attendance_record_job.rb b/app/jobs/student_join_attendance_record_job.rb index 32807bbec..8840ae142 100644 --- a/app/jobs/student_join_attendance_record_job.rb +++ b/app/jobs/student_join_attendance_record_job.rb @@ -4,14 +4,14 @@ class StudentJoinAttendanceRecordJob < ApplicationJob def perform(member_id) member = CourseMember.find_by(id: member_id) course = member&.course - return if member.blank? || course.blank? + return if member.blank? || member.role != "STUDENT" || course.blank? current_date = Date.current current_end_time = Time.current.strftime("%H:%M:%S") group_ids = member.course_group_id == 0 ? [0] : [member.course_group_id, 0] current_attendance_ids = course.course_attendances.joins(:course_attendance_groups).where(course_attendance_groups: {course_group_id: group_ids}). - where("(attendance_date = '#{current_date}' and start_time <= '#{current_end_time}' and end_time > '#{current_end_time}') or (attendance_date > '#{current_date}')").pluck(:id) + where("(attendance_date = '#{current_date}' and end_time > '#{current_end_time}') or (attendance_date > '#{current_date}')").pluck(:id) all_group_attendance_ids = course.course_attendances.joins(:course_attendance_groups).where(course_attendance_groups: {course_group_id: 0}).pluck(:id) member.course_member_attendances.where.not(course_attendance_id: all_group_attendance_ids+current_attendance_ids).delete_all diff --git a/app/models/course.rb b/app/models/course.rb index abe17b3cd..a0daaea22 100644 --- a/app/models/course.rb +++ b/app/models/course.rb @@ -226,7 +226,7 @@ class Course < ApplicationRecord end def all_course_module_types - %w[activity announcement online_learning shixun_homework common_homework group_homework exercise attachment course_group graduation poll board statistics video] + %w[activity announcement online_learning shixun_homework common_homework group_homework exercise attachment course_group graduation poll board statistics video attendance] end def get_course_module_by_type(type) @@ -392,7 +392,7 @@ class Course < ApplicationRecord # 课堂实训作业的评测次数 def evaluate_count course_user_ids = students.pluck(:user_id) - shixun_ids = homework_commons.joins(:homework_commons_shixun).where(homework_type: 4).pluck(:shixun_id) + shixun_ids = homework_commons.joins(:homework_commons_shixun).where(homework_type: "practice").pluck(:shixun_id) return 0 if shixun_ids.blank? Game.joins(:challenge).where(challenges: {shixun_id: shixun_ids}, games: {user_id: course_user_ids}).sum(:evaluate_count) end @@ -429,6 +429,7 @@ class Course < ApplicationRecord when 'video' then '视频直播' when 'board' then '讨论' when 'course_group' then '分班' + when 'attendance' then '签到' when 'statistics' then '统计' else '' end @@ -449,7 +450,8 @@ class Course < ApplicationRecord when 'video' then 11 when 'board' then 12 when 'course_group' then 13 - when 'statistics' then 14 + when 'attendance' then 14 + when 'statistics' then 15 else 100 end end diff --git a/app/models/course_member.rb b/app/models/course_member.rb index 21221e7e9..0a0433f6f 100644 --- a/app/models/course_member.rb +++ b/app/models/course_member.rb @@ -165,7 +165,9 @@ class CourseMember < ApplicationRecord private def create_attendance_record - StudentJoinAttendanceRecordJob.perform_later(id) + if role == "STUDENT" + StudentJoinAttendanceRecordJob.perform_later(id) + end end end diff --git a/app/models/course_module.rb b/app/models/course_module.rb index 32a6a7794..fec22b697 100644 --- a/app/models/course_module.rb +++ b/app/models/course_module.rb @@ -4,6 +4,7 @@ class CourseModule < ApplicationRecord # 二级目录 has_many :course_second_categories + has_many :first_categories, -> { first_categories }, class_name: "CourseSecondCategory" validates :module_name, length: { maximum: 20, too_long: "不能超过20个字符" } @@ -17,12 +18,14 @@ class CourseModule < ApplicationRecord scope :shixun_homework_module, -> { where(module_type: 'shixun_homework') } scope :search_by_module_type, -> (type) {where(module_type:type)} - # 课堂模块的子目录 - def course_second_categories + after_create :create_graduation_module + + private + + def create_graduation_module if module_type == "graduation" && CourseSecondCategory.where(course_module_id: self.id).count == 0 CourseSecondCategory.create!(course_module_id: self.id, course_id: self.course_id, name: "毕设选题", category_type: "graduation", position: 1) CourseSecondCategory.create!(course_module_id: self.id, course_id: self.course_id, name: "毕设任务", category_type: "graduation", position: 2) end - CourseSecondCategory.where(course_module_id: self.id) end end diff --git a/app/models/course_second_category.rb b/app/models/course_second_category.rb index 84b47e27f..3306cd003 100644 --- a/app/models/course_second_category.rb +++ b/app/models/course_second_category.rb @@ -3,7 +3,12 @@ class CourseSecondCategory < ApplicationRecord belongs_to :course belongs_to :course_module + belongs_to :parent, class_name: "CourseSecondCategory", foreign_key: "parent_id", optional: true + has_many :homework_commons + has_many :children, -> { order(position: :asc ) }, class_name: "CourseSecondCategory", foreign_key: "parent_id", dependent: :destroy + + scope :first_categories, -> { where(parent_id: 0) } validates :name, length: { maximum: 60, too_long: "不能超过60个字符" } diff --git a/app/queries/weapps/subject_query.rb b/app/queries/weapps/subject_query.rb index 0a3c9beb2..180176ce2 100644 --- a/app/queries/weapps/subject_query.rb +++ b/app/queries/weapps/subject_query.rb @@ -8,7 +8,7 @@ class Weapps::SubjectQuery < ApplicationQuery end def call - subjects = @current_laboratory.subjects.unhidden.publiced + subjects = @current_laboratory.subjects.unhidden.publiced.show_moblied # 课程体系的过滤 if params[:sub_discipline_id].present? @@ -21,7 +21,7 @@ class Weapps::SubjectQuery < ApplicationQuery # 搜索 if params[:keyword].present? - subjects = subjects.where("subjects.name like '%#{params[:keyword]}%'") + subjects = subjects.where("subjects.name like :keyword", keyword: "%#{params[:keyword]}%") end subjects = subjects.left_joins(:shixuns, :repertoire).select('subjects.id, subjects.name, subjects.excellent, subjects.stages_count, subjects.status, subjects.homepage_show, @@ -33,10 +33,10 @@ class Weapps::SubjectQuery < ApplicationQuery private def order_type - params[:order] || "updated_at" + params[:order] == "updated_at" ? "updated_at" : "myshixuns_count" end def sort_type - params[:sort] || "desc" + params[:sort] == "desc" ? "desc" : "asc" end end \ No newline at end of file diff --git a/app/services/create_watch_video_service.rb b/app/services/create_watch_video_service.rb index afdd57b5b..0f1b32814 100644 --- a/app/services/create_watch_video_service.rb +++ b/app/services/create_watch_video_service.rb @@ -10,29 +10,36 @@ class CreateWatchVideoService < ApplicationService def call ActiveRecord::Base.transaction do current_time = Time.now + params[:watch_duration] = params[:watch_duration].to_f.round(2) + params[:total_duration] = params[:total_duration].to_f.round(2) + params[:duration] = params[:duration].to_f.round(2) + if params[:log_id].present? - if params[:total_duration].to_f < params[:watch_duration].to_f || params[:watch_duration].to_f < 0 - raise Error, '观看时长错误' + watch_video_history = user.watch_video_histories.find(params[:log_id]) + if params[:total_duration] < params[:watch_duration] + return watch_video_history end # 更新观看时长 - watch_video_history = user.watch_video_histories.find(params[:log_id]) - - if watch_video_history.present? && watch_video_history.watch_duration <= params[:watch_duration].to_f && params[:total_duration].to_f > watch_video_history.total_duration + if watch_video_history.present? && !watch_video_history.is_finished && watch_video_history.watch_duration <= params[:watch_duration] && watch_video_history.total_duration <= params[:total_duration] # 如果观看总时长没变,说明视频没有播放,无需再去记录 - watch_video_history.end_at = current_time watch_video_history.total_duration = params[:total_duration] - watch_video_history.watch_duration = params[:watch_duration].to_f > watch_video_history.duration ? watch_video_history.duration : params[:watch_duration] - watch_video_history.is_finished = (watch_video_history.duration <= params[:watch_duration].to_f) + watch_video_history.watch_duration = params[:watch_duration] > watch_video_history.duration ? watch_video_history.duration : params[:watch_duration] + watch_video_history.is_finished = params[:ed].present? watch_video_history.save! watch_course_video = watch_video_history.watch_course_video - if watch_course_video.present? && !watch_course_video.is_finished && watch_course_video.watch_duration < params[:watch_duration].to_f - # 更新课程视频的时长及是否看完状态 - watch_course_video.watch_duration = params[:watch_duration] - watch_course_video.is_finished = (watch_course_video.duration <= params[:watch_duration].to_f) + if watch_course_video.present? + watch_course_video.total_duration = watch_course_video.watch_video_histories.sum(:total_duration) watch_course_video.end_at = current_time + if !watch_course_video.is_finished && watch_course_video.watch_duration < params[:watch_duration] + # 更新课程视频的时长及是否看完状态 + watch_course_video.watch_duration = params[:watch_duration] + if params[:ed].present? + watch_course_video.is_finished = watch_course_video.total_duration >= watch_course_video.duration + end + end watch_course_video.save! end end @@ -45,11 +52,10 @@ class CreateWatchVideoService < ApplicationService d.start_at = current_time d.duration = params[:duration] end - - watch_video_history = build_video_log(current_time, course_video.video_id, watch_course_video.id) - watch_video_history.save! watch_course_video.save! unless watch_course_video.persisted? + watch_video_history = build_video_log(current_time, course_video.video_id, watch_course_video.id) + watch_video_history.save! else # 非课堂视频 video = Video.find(params[:video_id]) diff --git a/app/services/weapps/shixun_search_service.rb b/app/services/weapps/shixun_search_service.rb index 448bb1992..9fd7cce99 100644 --- a/app/services/weapps/shixun_search_service.rb +++ b/app/services/weapps/shixun_search_service.rb @@ -34,11 +34,18 @@ class Weapps::ShixunSearchService < ApplicationService unless params[:keyword].blank? keyword = params[:keyword].strip shixuns = shixuns.joins(:user). - where("concat(lastname, firstname) like :keyword or shixuns.name like :keyword", - keyword: "%#{keyword}%", name: "%#{keyword.split(" ").join("%")}%").distinct + where("concat(lastname, firstname) like :keyword or shixuns.name like :keyword", + keyword: "%#{keyword}%", name: "%#{keyword.split(" ").join("%")}%").distinct end - shixuns.order("#{sort_str} #{order_str}") + order = + if sort_str == "wechat_myshixuns_count" + "is_wechat_support desc, myshixuns_count #{order_str}" + else + "#{sort_str} #{order_str}" + end + + shixuns.order(order) end private diff --git a/app/views/admins/courses/shared/_td.html.erb b/app/views/admins/courses/shared/_td.html.erb index 87e6f305d..7b72179da 100644 --- a/app/views/admins/courses/shared/_td.html.erb +++ b/app/views/admins/courses/shared/_td.html.erb @@ -5,9 +5,9 @@ <%= course.course_members_count %> <%= get_attachment_count(course, 0) %> -<%= course.course_homework_count(1) %> -<%= course.course_homework_count(3) %> -<%= course.course_homework_count(4) %> +<%= course.course_homework_count("normal") %> +<%= course.course_homework_count("group") %> +<%= course.course_homework_count("practice") %> <%= course.exercises_count %> <%= course.evaluate_count %> <%= course.is_public == 1 ? "--" : "√" %> diff --git a/app/views/attendances/index.json.jbuilder b/app/views/attendances/index.json.jbuilder index 8a4dd2bb1..b44f9a4fe 100644 --- a/app/views/attendances/index.json.jbuilder +++ b/app/views/attendances/index.json.jbuilder @@ -1,5 +1,5 @@ json.attendances @attendances do |attendance| - json.(attendance, :id, :name, :normal_count, :all_count, :mode) + json.(attendance, :id, :name, :normal_count, :all_count, :mode, :attendance_code) json.author do user = attendance.user json.user_name user.real_name diff --git a/app/views/course_modules/show.json.jbuilder b/app/views/course_modules/show.json.jbuilder index 9d70797b8..e5a86c42f 100644 --- a/app/views/course_modules/show.json.jbuilder +++ b/app/views/course_modules/show.json.jbuilder @@ -2,7 +2,10 @@ json.course_module do json.id @course_module.id json.module_name @course_module.module_name json.module_type @course_module.module_type - json.course_second_categories do - json.array! @course_module.course_second_categories, :id, :name + json.course_second_categories @course_module.first_categories do |category| + json.(category, :id, :name) + json.course_third_categories category.children do |child| + json.(child, :id, :name) + end end end \ No newline at end of file diff --git a/app/views/course_videos/watch_histories.json.jbuilder b/app/views/course_videos/watch_histories.json.jbuilder new file mode 100644 index 000000000..705e2d6b7 --- /dev/null +++ b/app/views/course_videos/watch_histories.json.jbuilder @@ -0,0 +1,12 @@ +json.data do + json.array! @watch_course_videos do |d| + json.user_name d.user&.real_name + json.is_finished d.is_finished ? true : false + json.total_duration d.total_duration.round(2) + json.feq d['freq'] + json.start_at d.start_at.to_s + json.end_at d.end_at.to_s + end +end + +json.count @count \ No newline at end of file diff --git a/app/views/courses/_category_info.json.jbuilder b/app/views/courses/_category_info.json.jbuilder new file mode 100644 index 000000000..3ecc5cdc6 --- /dev/null +++ b/app/views/courses/_category_info.json.jbuilder @@ -0,0 +1,6 @@ +json.category_id category.id +json.category_name category.name +json.position category.position +json.category_count category_task_count(@course, category, @user) +json.category_type category.category_type_str +json.second_category_url category_url(category, @course) \ No newline at end of file diff --git a/app/views/courses/attahcment_category_list.json.jbuilder b/app/views/courses/attahcment_category_list.json.jbuilder index 217b1092e..af83890a9 100644 --- a/app/views/courses/attahcment_category_list.json.jbuilder +++ b/app/views/courses/attahcment_category_list.json.jbuilder @@ -1,10 +1,17 @@ json.has_course_groups @has_course_groups json.course_modules do json.array! @course_modules do |course_module| - json.id course_module.id - json.module_name course_module.module_name - json.course_second_categories do - json.array! course_module.course_second_categories, :id, :name + json.value course_module.id + json.title course_module.module_name + json.children course_module.first_categories do |category| + json.title category.name + json.value category.id + unless @no_child + json.children category.children do |child| + json.title child.name + json.value child.id + end + end end end end diff --git a/app/views/courses/course_videos.json.jbuilder b/app/views/courses/course_videos.json.jbuilder index bee5e89cb..c2a4c1282 100644 --- a/app/views/courses/course_videos.json.jbuilder +++ b/app/views/courses/course_videos.json.jbuilder @@ -10,6 +10,8 @@ json.videos @videos do |video| json.user_login user&.login else json.partial! 'users/videos/video', locals: { video: video.video } + json.total_time video.watch_course_videos.sum(:total_duration).round(0) + json.people_num video.watch_course_videos.count(:user_id) end end diff --git a/app/views/courses/index.json.jbuilder b/app/views/courses/index.json.jbuilder index 8a0d06475..f9bceebb8 100644 --- a/app/views/courses/index.json.jbuilder +++ b/app/views/courses/index.json.jbuilder @@ -2,7 +2,7 @@ json.courses @courses do |course| json.id course.id json.name course.name json.avatar_url url_to_avatar(course.teacher) - json.creator course.teacher.real_name + json.creator course.teacher&.real_name json.school course.school&.name json.technical_title "" # course.teacher.identity json.course_members_count course.course_members_count diff --git a/app/views/courses/left_banner.json.jbuilder b/app/views/courses/left_banner.json.jbuilder index ecd8fe127..b53dbbb79 100644 --- a/app/views/courses/left_banner.json.jbuilder +++ b/app/views/courses/left_banner.json.jbuilder @@ -25,13 +25,12 @@ json.course_modules @course_modules.each do |mod| end end else - json.second_category mod.course_second_categories.each do |category| - json.category_id category.id - json.category_name category.name - json.position category.position - json.category_count category_task_count(@course, category, @user) - json.category_type category.category_type_str - json.second_category_url category_url(category, @course) + json.second_category mod.first_categories.each do |category| + json.partial! "category_info", category: category + + json.third_category category.children do |child| + json.partial! "category_info", category: child + end end end end diff --git a/app/views/courses/own_watch_histories.json.jbuilder b/app/views/courses/own_watch_histories.json.jbuilder new file mode 100644 index 000000000..982939d9c --- /dev/null +++ b/app/views/courses/own_watch_histories.json.jbuilder @@ -0,0 +1,13 @@ +json.data do + json.array! @videos.each do |d| + json.title d.title + json.user_name @current_user&.real_name + json.is_finished d.is_finished ? true : false + json.total_duration d.total_duration.round(2) + json.freq d['freq'] + json.start_at d.start_at.to_s + json.end_at d.end_at.to_s + end +end + +json.count @count \ No newline at end of file diff --git a/app/views/courses/watch_video_histories.json.jbuilder b/app/views/courses/watch_video_histories.json.jbuilder new file mode 100644 index 000000000..207b9029b --- /dev/null +++ b/app/views/courses/watch_video_histories.json.jbuilder @@ -0,0 +1,10 @@ +json.videos do + json.array! @videos do |v| + json.id v.id + json.title v.title + json.user_name v.user&.real_name + json.people_num v['people_num'] + json.total_time v['total_time'] + end +end +json.count @count \ No newline at end of file diff --git a/app/views/files/index.json.jbuilder b/app/views/files/index.json.jbuilder index e0c21e232..07430cd7b 100644 --- a/app/views/files/index.json.jbuilder +++ b/app/views/files/index.json.jbuilder @@ -7,6 +7,7 @@ json.data do json.unpublish_count @unpublish_count json.unlink_count @unlink_count json.course_is_public @course.is_public? + json.parent_category_id @parent_category_id json.files do json.array! @attachments do |attachment| json.is_history_file attachment.attachment_histories.count > 0 #是否有历史文件 @@ -16,7 +17,7 @@ json.data do end # json.partial! "files/course_groups", attachment_group_settings: attachment.attachment_group_settings json.category_id attachment.course_second_category_id - if @course_second_category_id.to_i == 0 + unless @parent_category_id.present? && @parent_category_id != 0 json.category_name attachment.course_second_category&.name end end diff --git a/app/views/homework_commons/publish_groups.json.jbuilder b/app/views/homework_commons/publish_groups.json.jbuilder index f3a99dbca..b77d14fe7 100644 --- a/app/views/homework_commons/publish_groups.json.jbuilder +++ b/app/views/homework_commons/publish_groups.json.jbuilder @@ -3,4 +3,5 @@ json.course_groups @course_groups do |group| json.name group.name json.end_time @group_settings.select{|group_setting| group_setting.course_group_id == group.id}.first&.end_time end -json.end_time @homework.end_time \ No newline at end of file +json.end_time @homework.end_time +json.late_time @homework.late_time \ No newline at end of file diff --git a/app/views/shixuns/_top.json.jbuilder b/app/views/shixuns/_top.json.jbuilder index c90de5e5b..362cfd15b 100644 --- a/app/views/shixuns/_top.json.jbuilder +++ b/app/views/shixuns/_top.json.jbuilder @@ -14,7 +14,7 @@ json.name shixun.name json.stu_num shixun.myshixuns_count json.experience shixun.all_score json.diffcult level_to_s(shixun.trainee) -json.score_info shixun.averge_star # todo: 这块可以改成只显示实训的平均分,不用每次都去取每种星的分数了。 +json.score_info shixun.shixun_preference_info # todo: 这块可以改成只显示实训的平均分,不用每次都去取每种星的分数了。 json.is_jupyter shixun.is_jupyter # 用于是否显示导航栏中的'背景知识' json.propaedeutics shixun.propaedeutics.present? diff --git a/app/views/weapps/attendances/show.json.jbuilder b/app/views/weapps/attendances/show.json.jbuilder index b3f13ca60..b16971741 100644 --- a/app/views/weapps/attendances/show.json.jbuilder +++ b/app/views/weapps/attendances/show.json.jbuilder @@ -9,6 +9,10 @@ json.name @attendance.name json.attendance_date @attendance.attendance_date.strftime("%Y-%m-%d") json.start_time @attendance.start_time.strftime("%H:%M") json.end_time @attendance.end_time.strftime("%H:%M") +json.author do + json.user_name @user.real_name + json.user_login @user.login +end # json.course_groups @group_ids do |group| # json.(group, :id, :name, :course_members_count) diff --git a/app/views/weapps/course_member_attendances/index.json.jbuilder b/app/views/weapps/course_member_attendances/index.json.jbuilder index cec70b3fe..765de5833 100644 --- a/app/views/weapps/course_member_attendances/index.json.jbuilder +++ b/app/views/weapps/course_member_attendances/index.json.jbuilder @@ -4,8 +4,9 @@ # json.student_id member.user&.student_id # end -json.member_attendances @members.each do |member| +json.member_attendances @members.each_with_index.to_a do |member, index| json.(member, :user_id) + json.index (@page.to_i - 1) * @limit.to_i + index + 1 json.user_name member.user&.real_name json.student_id member.user&.student_id json.attendance_status @member_attendances.select{|attendance| attendance.course_member_id == member.id}.first&.attendance_status || "ABSENCE" diff --git a/app/views/weapps/shixun_lists/index.json.jbuilder b/app/views/weapps/shixun_lists/index.json.jbuilder index fac238f45..10b66fdfa 100644 --- a/app/views/weapps/shixun_lists/index.json.jbuilder +++ b/app/views/weapps/shixun_lists/index.json.jbuilder @@ -5,5 +5,6 @@ json.shixun_list @results do |obj| json.study_count obj.myshixuns_count json.author_name obj.user.real_name json.author_img url_to_avatar(obj.user) + json.pic url_to_avatar(obj) end json.shixuns_count @total_count \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 6c3efcfe8..047141ea8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -531,6 +531,9 @@ Rails.application.routes.draw do post :inform_up post :inform_down get :calculate_all_shixun_scores + get :watch_video_histories + get :watch_statics + get :own_watch_histories end collection do @@ -541,7 +544,11 @@ Rails.application.routes.draw do get 'search_slim' end - resources :course_videos, only:[:create, :update], shallow: true + resources :course_videos, only:[:create, :update], shallow: true do + member do + get :watch_histories + end + end resources :course_stages, shallow: true do member do diff --git a/db/migrate/20200312075912_add_attendance_to_course_module.rb b/db/migrate/20200312075912_add_attendance_to_course_module.rb new file mode 100644 index 000000000..02bb48fe1 --- /dev/null +++ b/db/migrate/20200312075912_add_attendance_to_course_module.rb @@ -0,0 +1,16 @@ +class AddAttendanceToCourseModule < ActiveRecord::Migration[5.2] + def change + Course.all.each do |course| + unless course.course_modules.exists?(module_type: "attendance") + atta_position = course.course_modules.find_by(module_type: 'course_group')&.position.to_i + attendance_position = atta_position != 0 ? (atta_position + 1) : 14 + course.course_modules.where("position >= #{attendance_position}").update_all("position = position + 1") + if course.is_end + course.course_modules << CourseModule.new(module_type: "attendance", hidden: 1, module_name: "签到", position: attendance_position) + else + course.course_modules << CourseModule.new(module_type: "attendance", hidden: 0, module_name: "签到", position: attendance_position) + end + end + end + end +end diff --git a/db/migrate/20200313030919_add_parent_id_to_second_category.rb b/db/migrate/20200313030919_add_parent_id_to_second_category.rb new file mode 100644 index 000000000..ab45c4025 --- /dev/null +++ b/db/migrate/20200313030919_add_parent_id_to_second_category.rb @@ -0,0 +1,7 @@ +class AddParentIdToSecondCategory < ActiveRecord::Migration[5.2] + def change + add_column :course_second_categories, :parent_id, :integer, default: 0 + + add_index :course_second_categories, :parent_id + end +end diff --git a/db/migrate/20200313104522_add_total_duration_to_watch_course_duration.rb b/db/migrate/20200313104522_add_total_duration_to_watch_course_duration.rb new file mode 100644 index 000000000..741be91f5 --- /dev/null +++ b/db/migrate/20200313104522_add_total_duration_to_watch_course_duration.rb @@ -0,0 +1,8 @@ +class AddTotalDurationToWatchCourseDuration < ActiveRecord::Migration[5.2] + def change + #add_column :watch_course_videos, :total_duration, :float, default: 0 + #WatchVideoHistory.where("created_at < '2020-03-14 00:00:00'").each do |d| + # d.watch_course_video.increment!(:total_duration, d.total_duration) if d.watch_course_video.present? + #end + end +end diff --git a/lib/tasks/video_transcode.rake b/lib/tasks/video_transcode.rake index 4fb7c17bd..2b4a1eac7 100644 --- a/lib/tasks/video_transcode.rake +++ b/lib/tasks/video_transcode.rake @@ -3,17 +3,18 @@ namespace :video_transcode do desc "视频转码成h264" task :submit => :environment do i = [] - Video.where.not(uuid: nil, file_url: nil).where(transcoded: false, status: "published").find_each do |v| + Video.where.not(uuid: nil, file_url: nil).where(transcoded: false).find_each do |v| code_info = AliyunVod::Service.get_meta_code_info(v.uuid) if v.file_url.include?('.mp4') && code_info[:codecnamne]&.include?("h264") v.update(transcoded: true) else puts("uuid: #{v.uuid}") i << "#{v.id}, #{v.file_url}, #{code_info[:codecnamne]}" - AliyunVod::Service.submit_transcode_job(v.uuid, 'a0277c5c0c7458458e171b0cee6ebf5e') + AliyunVod::Service.submit_transcode_job(v.uuid, 'a0277c5c0c7458458e171b0cee6ebf5e') rescue nil end end puts "###########转码个数:#{i.size}" puts "###########id,file_url, codecnamne:#{i}" + Video.where(transcoded: false).update_all(transcoded: true) end end \ No newline at end of file diff --git a/public/images/qiandao/daoke.png b/public/images/qiandao/daoke.png new file mode 100644 index 000000000..7bdcac50e Binary files /dev/null and b/public/images/qiandao/daoke.png differ diff --git a/public/images/qiandao/kuangke.png b/public/images/qiandao/kuangke.png new file mode 100644 index 000000000..13c9972ee Binary files /dev/null and b/public/images/qiandao/kuangke.png differ diff --git a/public/images/qiandao/lishi.png b/public/images/qiandao/lishi.png new file mode 100644 index 000000000..98c72702a Binary files /dev/null and b/public/images/qiandao/lishi.png differ diff --git a/public/images/qiandao/qingjia.png b/public/images/qiandao/qingjia.png new file mode 100644 index 000000000..2d885f432 Binary files /dev/null and b/public/images/qiandao/qingjia.png differ diff --git a/public/images/qiandao/shipingshi.png b/public/images/qiandao/shipingshi.png new file mode 100755 index 000000000..984f61862 Binary files /dev/null and b/public/images/qiandao/shipingshi.png differ diff --git a/public/images/qiandao/spcishu.png b/public/images/qiandao/spcishu.png new file mode 100755 index 000000000..97425be78 Binary files /dev/null and b/public/images/qiandao/spcishu.png differ diff --git a/public/images/qiandao/sprenshu.png b/public/images/qiandao/sprenshu.png new file mode 100755 index 000000000..cfe02b72e Binary files /dev/null and b/public/images/qiandao/sprenshu.png differ diff --git a/public/react/package-lock.json b/public/react/package-lock.json index a22a5a9ea..ce3ac05c7 100644 --- a/public/react/package-lock.json +++ b/public/react/package-lock.json @@ -3323,6 +3323,25 @@ "object-assign": "^4.1.1" } }, + "create-react-context": { + "version": "0.3.0", + "resolved": "https://registry.npm.taobao.org/create-react-context/download/create-react-context-0.3.0.tgz", + "integrity": "sha1-VG3t6dxCLe8NP8L+A6/gvA9PfYw=", + "requires": { + "gud": "^1.0.0", + "warning": "^4.0.3" + }, + "dependencies": { + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npm.taobao.org/warning/download/warning-4.0.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwarning%2Fdownload%2Fwarning-4.0.3.tgz", + "integrity": "sha1-Fungd+uKhtavfWSqHgX9hbRnjKM=", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, "cropperjs": { "version": "0.7.2", "resolved": "https://registry.npm.taobao.org/cropperjs/download/cropperjs-0.7.2.tgz", @@ -13577,6 +13596,21 @@ "resolved": "http://registry.npm.taobao.org/q/download/q-1.5.1.tgz", "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" }, + "qr.js": { + "version": "0.0.0", + "resolved": "https://registry.npm.taobao.org/qr.js/download/qr.js-0.0.0.tgz", + "integrity": "sha1-ys6GOG9ZoNuAUPqQ2baw6IoeNk8=" + }, + "qrcode.react": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/qrcode.react/download/qrcode.react-1.0.0.tgz", + "integrity": "sha1-foiJ2zt2nlVejrRj1MbeIhw21d4=", + "requires": { + "loose-envify": "^1.4.0", + "prop-types": "^15.6.0", + "qr.js": "0.0.0" + } + }, "qs": { "version": "6.7.0", "resolved": "http://registry.npm.taobao.org/qs/download/qs-6.7.0.tgz", @@ -14544,6 +14578,73 @@ "object-assign": "^4.1.1" } }, + "react-datepicker": { + "version": "2.14.0", + "resolved": "https://registry.npm.taobao.org/react-datepicker/download/react-datepicker-2.14.0.tgz", + "integrity": "sha1-zbLyNvEgp+bpc7YX9xNYtchheqw=", + "requires": { + "classnames": "^2.2.6", + "date-fns": "^2.0.1", + "prop-types": "^15.7.2", + "react-onclickoutside": "^6.9.0", + "react-popper": "^1.3.4" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.8.7", + "resolved": "https://registry.npm.taobao.org/@babel/runtime/download/@babel/runtime-7.8.7.tgz?cache=0&sync_timestamp=1583373718614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fruntime%2Fdownload%2F%40babel%2Fruntime-7.8.7.tgz", + "integrity": "sha1-j+/OmALbVIgbpZ+Quyhxm0mWMk0=", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "date-fns": { + "version": "2.11.0", + "resolved": "https://registry.npm.taobao.org/date-fns/download/date-fns-2.11.0.tgz?cache=0&sync_timestamp=1584132572779&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdate-fns%2Fdownload%2Fdate-fns-2.11.0.tgz", + "integrity": "sha1-7CtEl3RludyzcAIdXmwBmxnzbQY=" + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/deep-equal/download/deep-equal-1.1.1.tgz", + "integrity": "sha1-tcmMlCzv+vfLBR4k4UNKJaLmB2o=", + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "react-popper": { + "version": "1.3.7", + "resolved": "https://registry.npm.taobao.org/react-popper/download/react-popper-1.3.7.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-popper%2Fdownload%2Freact-popper-1.3.7.tgz", + "integrity": "sha1-9qNHE2LvHw0QpJY2c3id4brKIyQ=", + "requires": { + "@babel/runtime": "^7.1.2", + "create-react-context": "^0.3.0", + "deep-equal": "^1.1.1", + "popper.js": "^1.14.4", + "prop-types": "^15.6.1", + "typed-styles": "^0.0.7", + "warning": "^4.0.2" + } + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npm.taobao.org/regenerator-runtime/download/regenerator-runtime-0.13.5.tgz?cache=0&sync_timestamp=1584052597708&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fregenerator-runtime%2Fdownload%2Fregenerator-runtime-0.13.5.tgz", + "integrity": "sha1-2Hih0JS0MG0QuQlkhLM+vVXiZpc=" + }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npm.taobao.org/warning/download/warning-4.0.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwarning%2Fdownload%2Fwarning-4.0.3.tgz", + "integrity": "sha1-Fungd+uKhtavfWSqHgX9hbRnjKM=", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, "react-dev-utils": { "version": "5.0.3", "resolved": "http://registry.npm.taobao.org/react-dev-utils/download/react-dev-utils-5.0.3.tgz", @@ -14703,6 +14804,11 @@ } } }, + "react-onclickoutside": { + "version": "6.9.0", + "resolved": "https://registry.npm.taobao.org/react-onclickoutside/download/react-onclickoutside-6.9.0.tgz", + "integrity": "sha1-pUvDF66M9hMaXXis6lWhEGfzeh8=" + }, "react-player": { "version": "1.15.2", "resolved": "https://registry.npm.taobao.org/react-player/download/react-player-1.15.2.tgz", @@ -17467,6 +17573,11 @@ "mime-types": "~2.1.18" } }, + "typed-styles": { + "version": "0.0.7", + "resolved": "https://registry.npm.taobao.org/typed-styles/download/typed-styles-0.0.7.tgz", + "integrity": "sha1-kzkqAIeUxFlRGf9i3eaAnbxAo9k=" + }, "typedarray": { "version": "0.0.6", "resolved": "http://registry.npm.taobao.org/typedarray/download/typedarray-0.0.6.tgz", diff --git a/public/react/package.json b/public/react/package.json index d29adb905..e21a44df3 100644 --- a/public/react/package.json +++ b/public/react/package.json @@ -61,6 +61,7 @@ "postcss-loader": "2.0.8", "promise": "8.0.1", "prop-types": "^15.6.1", + "qrcode.react": "^1.0.0", "qs": "^6.6.0", "quill": "^1.3.7", "quill-delta-to-html": "^0.11.0", @@ -77,6 +78,7 @@ "react-codemirror2": "^6.0.0", "react-content-loader": "^3.1.1", "react-cookies": "^0.1.1", + "react-datepicker": "^2.14.0", "react-dev-utils": "^5.0.0", "react-dom": "^16.9.0", "react-hot-loader": "^4.0.0", diff --git a/public/react/public/css/demo_index.html b/public/react/public/css/demo_index.html index 7b0854698..7a6d6b5e8 100644 --- a/public/react/public/css/demo_index.html +++ b/public/react/public/css/demo_index.html @@ -30,6 +30,36 @@
+ : + this.statisticsy(b)}> + } + { + statistics===false? { - type === "video" ? - : @@ -355,6 +371,9 @@ class VideoIndex extends Component{ > } + : + "" + } ) } diff --git a/public/react/src/modules/courses/Video/video-play/index.jsx b/public/react/src/modules/courses/Video/video-play/index.jsx index 8972c588d..e33439dcf 100644 --- a/public/react/src/modules/courses/Video/video-play/index.jsx +++ b/public/react/src/modules/courses/Video/video-play/index.jsx @@ -1,5 +1,26 @@ import React, { useRef, useEffect, useCallback } from 'react' +Object.defineProperty(HTMLMediaElement.prototype, 'playing', { + get: function () { + return !!(this.currentTime > 0 && !this.paused && !this.ended && this.readyState > 2) + } +}) + +function compareNumbers(a, b) { + return a - b; +} + +function getTotalEffectTime(pos) { + pos.sort(compareNumbers) + let sum = 0 + for (let i = 0; i < pos.length - 1; i++) { + let v = pos[i + 1] - pos[i] + if (v < 21) { + sum += v + } + } + return sum +} const regex = /(android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini)/i //接口文档 https://www.showdoc.cc/educoder?page_id=4029884447803706 export default ({ src, videoId, logWatchHistory, courseId = null }) => { @@ -12,22 +33,21 @@ export default ({ src, videoId, logWatchHistory, courseId = null }) => { const device = deviceMatch ? deviceMatch[0] : 'pc' let totalDuration = 0 - let totalTimePlayed = 0 let sumTimePlayed = 0 let lastUpdatedTime = 0 - let lastEffectUpdatedTime = 0 let logId = null let initLog = false let timeTick = 20 //记录频率 默认20s let logCount = 1 let isLoging = false let isSeeking = false + let pos = []//播放时间点集 - const log = useCallback((callback) => { + const log = useCallback((callback, isEnd = false) => { let params = {} if (logId) { params['log_id'] = logId - params['watch_duration'] = totalTimePlayed //当前观看视频时长,拖放进度条,重复的视频片段观看时,不会把重复的时长累积进来,最大时长是视频的总时长 + params['watch_duration'] = getTotalEffectTime(pos) //当前观看视频时长,拖放进度条,重复的视频片段观看时,不会把重复的时长累积进来,最大时长是视频的总时长 params['total_duration'] = sumTimePlayed //累计观看视频时长,拖放进度条,重复的视频片段观看时,重复观看时长要累积进来 } else { if (courseId) { @@ -39,6 +59,9 @@ export default ({ src, videoId, logWatchHistory, courseId = null }) => { params['duration'] = totalDuration params['device'] = device } + if (isEnd) { + params['ed'] = "1" + } async function getLogId() { isLoging = true let id = await logWatchHistory(params) @@ -82,43 +105,40 @@ export default ({ src, videoId, logWatchHistory, courseId = null }) => { useEffect(() => { function onPlay() { + pos.push(el.current.currentTime) if (!initLog) { initLog = true log() } } - + //循环播放, 累计时长不能清空 async function onEnded() { log(() => { logId = null - logCount = 1 - totalTimePlayed = 0 lastUpdatedTime = 0 - sumTimePlayed = 0 initLog = false isLoging = false - lastEffectUpdatedTime = 0 isSeeking = false - }) + }, true) } function onTimeupdate() { if (!isSeeking) { let newTime = el.current.currentTime let timeDiff = newTime - lastUpdatedTime - let effectTimeDiff = newTime - lastEffectUpdatedTime - if (effectTimeDiff > 0) { - totalTimePlayed += effectTimeDiff - lastEffectUpdatedTime = newTime - } - sumTimePlayed += Math.abs(timeDiff) - lastUpdatedTime = newTime - - if (!isLoging) { - if (sumTimePlayed - logCount * timeTick >= 0) { - logCount++ - log() + //currenttime update before Seeking & Seeked fired + if (Math.abs(timeDiff) < 0.5) { + sumTimePlayed += Math.abs(timeDiff) + lastUpdatedTime = newTime + if (!isLoging) { + if (sumTimePlayed - logCount * timeTick >= 0) { + logCount++ + pos.push(lastUpdatedTime) + log() + } } + }else { + lastUpdatedTime = newTime } } @@ -126,11 +146,13 @@ export default ({ src, videoId, logWatchHistory, courseId = null }) => { function onSeeking() { isSeeking = true - lastUpdatedTime = el.current.currentTime - lastEffectUpdatedTime = el.current.currentTime } function onSeeked() { + if (el.current.playing) { + pos.push(el.current.currentTime, lastUpdatedTime) + } + lastUpdatedTime = el.current.currentTime isSeeking = false } @@ -155,6 +177,9 @@ export default ({ src, videoId, logWatchHistory, courseId = null }) => { el.current.removeEventListener('seeking', onSeeking) el.current.removeEventListener('seeked', onSeeked) el.current.removeEventListener('timeupdate', onTimeupdate) + if(el.current.playing) { + log() + } } }, [el, src]) diff --git a/public/react/src/modules/courses/Video/video.css b/public/react/src/modules/courses/Video/video.css index 7f1528bef..d03710f0e 100644 --- a/public/react/src/modules/courses/Video/video.css +++ b/public/react/src/modules/courses/Video/video.css @@ -176,4 +176,14 @@ } .wei_meet_info{ margin:0px auto; -} \ No newline at end of file +} +.tongjis{ + border: 1px solid #4CACFF; + padding-left: 10px; + padding-right: 10px; + padding-top: 5px; + padding-bottom: 5px; + border-radius: 5px; + line-height: 1px; + +} diff --git a/public/react/src/modules/courses/busyWork/CommonWorkAppraiseReviseAttachments.js b/public/react/src/modules/courses/busyWork/CommonWorkAppraiseReviseAttachments.js index 31fd4b9e6..7305591df 100644 --- a/public/react/src/modules/courses/busyWork/CommonWorkAppraiseReviseAttachments.js +++ b/public/react/src/modules/courses/busyWork/CommonWorkAppraiseReviseAttachments.js @@ -14,9 +14,9 @@ function CommonWorkAppraiseReviseAttachments(props) { } `}
- 补交附件 + 补交内容
- + {/* {age} */}
@@ -47,6 +47,6 @@ function CommonWorkAppraiseReviseAttachments(props) {
} ) - + } export default CommonWorkAppraiseReviseAttachments; \ No newline at end of file diff --git a/public/react/src/modules/courses/busyWork/PublishRightnow.js b/public/react/src/modules/courses/busyWork/PublishRightnow.js index 9863a95d6..54e87a4c5 100644 --- a/public/react/src/modules/courses/busyWork/PublishRightnow.js +++ b/public/react/src/modules/courses/busyWork/PublishRightnow.js @@ -24,7 +24,10 @@ class PublishRightnow extends Component{ Savesname:undefined, Cancel:undefined, Saves:undefined, - Topvalright:undefined + Topvalright:undefined, + modallate_time:undefined, + immediatelyopen:false, + starttimeslate_time:undefined } } open = (usingCheckBeforePost) => { @@ -70,6 +73,8 @@ class PublishRightnow extends Component{ Cancel:this.homeworkhide, Saves:this.homeworkstartend, typs:showdatatypes ? "start" : "end", + immediatelyopen:true, + modallate_time:showdatatypes?this.state.starttimeslate_time:undefined, }) } homeworkhide=()=>{ @@ -91,7 +96,9 @@ class PublishRightnow extends Component{ StudentList_value:undefined, addname:undefined, addnametype:false, - addnametab:undefined + addnametab:undefined, + modallate_time:undefined, + immediatelyopen:false }) } @@ -187,6 +194,7 @@ class PublishRightnow extends Component{ this.setState({ course_groups: response.data.course_groups, starttimesend:response.data.end_time===undefined||response.data.end_time===null||response.data.end_time===""?undefined:response.data.end_time, + starttimeslate_time:response.data.late_time===undefined||response.data.late_time===null||response.data.late_time===""?undefined:response.data.late_time, }) }) .catch(function (error) { @@ -260,6 +268,8 @@ class PublishRightnow extends Component{ starttimes={this.state.starttimes} starttimesend={this.state.starttimesend} typs={this.state.typs} + immediatelyopen={this.state.immediatelyopen} + modallate_time={this.state.starttimeslate_time} />:""} { showActionButton && { showdatatypes ? "立即发布" : "立即截止" } } diff --git a/public/react/src/modules/courses/coursesDetail/CoursesBanner.js b/public/react/src/modules/courses/coursesDetail/CoursesBanner.js index f54c99978..7e324fadc 100644 --- a/public/react/src/modules/courses/coursesDetail/CoursesBanner.js +++ b/public/react/src/modules/courses/coursesDetail/CoursesBanner.js @@ -89,7 +89,6 @@ class CoursesBanner extends Component { } HideAddcoursestypess=(i)=>{ - console.log("调用了"); this.setState({ Addcoursestypes:false, mydisplay:true, diff --git a/public/react/src/modules/courses/coursesDetail/CoursesLeftNav.js b/public/react/src/modules/courses/coursesDetail/CoursesLeftNav.js index 56694d9e5..4db80dd5f 100644 --- a/public/react/src/modules/courses/coursesDetail/CoursesLeftNav.js +++ b/public/react/src/modules/courses/coursesDetail/CoursesLeftNav.js @@ -1242,7 +1242,8 @@ class Coursesleftnav extends Component{ item.type==="board"?: item.type==="course_group"?: item.type==="statistics"?: - item.type==="video"?:"" + item.type==="attendance"?: + item.type==="video"?:"" } {/*||this.props.location.pathname===this.state.url&&key===this.state.indexs*/} @@ -1431,7 +1432,7 @@ class Coursesleftnav extends Component{ item.type==="attachment"?: item.type==="board"?: item.type==="course_group"?: - + item.type==="attendance"?: item.type==="video"?: item.type==="statistics"?:"" } diff --git a/public/react/src/modules/courses/coursesHome/CoursesHome.js b/public/react/src/modules/courses/coursesHome/CoursesHome.js index 3ad4e805f..965159832 100644 --- a/public/react/src/modules/courses/coursesHome/CoursesHome.js +++ b/public/react/src/modules/courses/coursesHome/CoursesHome.js @@ -2,13 +2,13 @@ import React, { Component, Fragment } from 'react'; import { getImageUrl } from 'educoder'; import CoursesHomeCard from "./CoursesHomeCard.js" import axios from 'axios'; -import { Pagination } from 'antd'; import LoadingSpin from '../../../common/LoadingSpin'; import UpgradeModals from '../../modals/UpgradeModals'; import './css/CoursesHome.css'; import '../../tpm/shixuns/shixun-keyword-list.scss'; import btnNew from './btn-new.png' import btnJoin from './btn-join.png' +import Pagination from '../../../components/mini-pagination' class CoursesHome extends Component { constructor(props) { diff --git a/public/react/src/modules/courses/coursesPublic/OneSelfOrderModal.js b/public/react/src/modules/courses/coursesPublic/OneSelfOrderModal.js index 1f7fec57a..c51b9a813 100644 --- a/public/react/src/modules/courses/coursesPublic/OneSelfOrderModal.js +++ b/public/react/src/modules/courses/coursesPublic/OneSelfOrderModal.js @@ -35,7 +35,10 @@ class OneSelfOrderModal extends Component{ group_ids:[], endtime:"", course_groups:undefined, - Checkboxtype:true + Checkboxtype:true, + endtimetypevalue:undefined, + endtimetypeid:undefined, + endtimetypename:undefined } } componentDidMount() { @@ -202,29 +205,35 @@ class OneSelfOrderModal extends Component{ } propsSaves=(ds,endtime)=>{ + + this.setState({ + endtimetype:false + }) let {course_groups}=this.state; if(this.props.typs=="end"){ this.props.Saves() }else{ - - if(this.props.typs!="end"){ - if(!endtime){ - this.setState({ - endtimetype:true, - endtimetypevalue:"截止时间不能为空" - }) - return - } - if(moment(endtime,"YYYY-MM-DD HH:mm") <= moment(this.props.starttime,"YYYY-MM-DD HH:mm")){ - this.setState({ - endtimetype:true, - endtimetypevalue:"必须晚于当前时间" - }) - return + if(!this.props.immediatelyopen){ + if(this.props.typs!="end"){ + if(!endtime){ + this.setState({ + endtimetype:true, + endtimetypevalue:"截止时间不能为空" + }) + return + } + if(moment(endtime,"YYYY-MM-DD HH:mm") <= moment(this.props.starttime,"YYYY-MM-DD HH:mm")){ + this.setState({ + endtimetype:true, + endtimetypevalue:"必须晚于当前时间" + }) + return + } } } + let type=false if(course_groups===undefined||course_groups.length===0){ this.props.Saves(ds,moment(handleDateString(endtime),"YYYY-MM-DD HH:mm").format("YYYY-MM-DD HH:mm")) @@ -238,6 +247,7 @@ class OneSelfOrderModal extends Component{ this.setState({ endtimetype:true, endtimetypeid:items.id, + endtimetypename:items.name, endtimetypevalue:"截止时间不能为空" }) return @@ -245,12 +255,29 @@ class OneSelfOrderModal extends Component{ // arr.push(moment(moment(handleDateString(this.props.staytime)).add(1, 'week')).format("YYYY-MM-DD HH:mm")) }else{ if(moment(items.end_time,"YYYY-MM-DD HH:mm") <= moment(this.props.starttime,"YYYY-MM-DD HH:mm")){ + type=true this.setState({ endtimetype:true, - endtimetypevalue:"必须晚于当前时间" + endtimetypeid:items.id, + endtimetypename:items.name, + endtimetypevalue:"截止时间必须晚于当前时间" }) return } + + if(this.props.modallate_time){ + if(moment(items.end_time,"YYYY-MM-DD HH:mm") >= moment(this.props.modallate_time,"YYYY-MM-DD HH:mm")){ + type=true + this.setState({ + endtimetype:true, + endtimetypeid:items.id, + endtimetypename:items.name, + endtimetypevalue:"截止时间必须晚于补交结束时间" + }) + return + } + } + arr.push(handleDateString(items.end_time)) } } diff --git a/public/react/src/modules/courses/exercise/Studentshavecompletedthelist.js b/public/react/src/modules/courses/exercise/Studentshavecompletedthelist.js index c3d4bf465..070ceeed2 100644 --- a/public/react/src/modules/courses/exercise/Studentshavecompletedthelist.js +++ b/public/react/src/modules/courses/exercise/Studentshavecompletedthelist.js @@ -295,7 +295,10 @@ class Studentshavecompletedthelist extends Component { ) }, { - title: '最终成绩', + title:最终成绩 + 鼠标停留具体分值上可查
+ 看得分明细
+ }>
, dataIndex: 'efficiencyscore', key: 'efficiencyscore', align: 'center', @@ -337,6 +340,22 @@ class Studentshavecompletedthelist extends Component { }}>{record.efficiencyscore} : + + +
{record.objective_score===undefined?"":record.objective_score === "--" ? 客观题得分:0分 : + 客观题得分:{record.objective_score}分} +
+ +
{record.subjective_score===undefined?"":record.subjective_score === "--" ? 主观题得分:0分 : + 主观题得分:{record.subjective_score}分} +
+ +
{record.efficiencyscore === "--" ? 最终成绩:0分 : + 最终成绩:{record.efficiencyscore}分} +
+ + + }> 90 ? { color: '#DD1717', textAlign: "center" @@ -350,6 +369,7 @@ class Studentshavecompletedthelist extends Component { color: '#747A7F', textAlign: "center" }}>{record.efficiencyscore} +
} ) @@ -587,7 +607,10 @@ class Studentshavecompletedthelist extends Component { ) }, { - title: '最终成绩', + title:最终成绩 + 鼠标停留具体分值上可查
+ 看得分明细
+ }>
, dataIndex: 'efficiencyscore', key: 'efficiencyscore', align: 'center', @@ -631,6 +654,22 @@ class Studentshavecompletedthelist extends Component { }}>{record.efficiencyscore} : + + +
{record.objective_score===undefined?"":record.objective_score === "--" ? 客观题得分:0分 : + 客观题得分:{record.objective_score}分} +
+ +
{record.subjective_score===undefined?"":record.subjective_score === "--" ? 主观题得分:0分 : + 主观题得分:{record.subjective_score}分} +
+ +
{record.efficiencyscore === "--" ? 最终成绩:0分 : + 最终成绩:{record.efficiencyscore}分} +
+ + + }> 90 ? { color: '#DD1717', textAlign: "center", @@ -648,6 +687,7 @@ class Studentshavecompletedthelist extends Component { textAlign: "center", width:"199px" }}>{record.efficiencyscore} +
} @@ -871,7 +911,10 @@ class Studentshavecompletedthelist extends Component { ) }, { - title: '最终成绩', + title: 最终成绩 + 鼠标停留具体分值上可查
+ 看得分明细
+ }>
, dataIndex: 'efficiencyscore', key: 'efficiencyscore', align: 'center', @@ -909,6 +952,22 @@ class Studentshavecompletedthelist extends Component { }}>{record.efficiencyscore} : + + +
{record.objective_score===undefined?"":record.objective_score === "--" ? 客观题得分:0分 : + 客观题得分:{record.objective_score}分} +
+ +
{record.subjective_score===undefined?"":record.subjective_score === "--" ? 主观题得分:0分 : + 主观题得分:{record.subjective_score}分} +
+ +
{record.efficiencyscore === "--" ? 最终成绩:0分 : + 最终成绩:{record.efficiencyscore}分} +
+ + + }> 90 ? { color: '#DD1717', textAlign: "center", @@ -922,6 +981,7 @@ class Studentshavecompletedthelist extends Component { color: '#747A7F', textAlign: "center", }}>{record.efficiencyscore} +
} ) @@ -1095,7 +1155,10 @@ class Studentshavecompletedthelist extends Component { ) }, { - title: '最终成绩', + title: 最终成绩 + 鼠标停留具体分值上可查
+ 看得分明细
+ }>
, dataIndex: 'efficiencyscore', key: 'efficiencyscore', align: 'center', @@ -1132,6 +1195,22 @@ class Studentshavecompletedthelist extends Component { }}>{record.efficiencyscore} : + + +
{record.objective_score===undefined?"":record.objective_score === "--" ? 客观题得分:0分 : + 客观题得分:{record.objective_score}分} +
+ +
{record.subjective_score===undefined?"":record.subjective_score === "--" ? 主观题得分:0分 : + 主观题得分:{record.subjective_score}分} +
+ +
{record.efficiencyscore === "--" ? 最终成绩:0分 : + 最终成绩:{record.efficiencyscore}分} +
+ + + }> 90 ? { color: '#DD1717', textAlign: "center", @@ -1145,6 +1224,7 @@ class Studentshavecompletedthelist extends Component { color: '#747A7F', textAlign: "center", }}>{record.efficiencyscore} +
} ) @@ -1499,6 +1579,8 @@ class Studentshavecompletedthelist extends Component { completion: exercise_users[i].objective_score === undefined ? "--" : exercise_users[i].objective_score === null ? "--" : exercise_users[i].objective_score === "" ? "--" : exercise_users[i].objective_score, levelscore: exercise_users[i].subjective_score === undefined ? "--" : exercise_users[i].subjective_score === null ? "--" : exercise_users[i].subjective_score === "" ? "--" : exercise_users[i].subjective_score, efficiencyscore: exercise_users[i].score === null ? "--" : exercise_users[i].score === "" ? "--" : exercise_users[i].score, + objective_score: exercise_users[i].objective_score === null ? "--" : exercise_users[i].objective_score === "" ? "--" : exercise_users[i].objective_score, + subjective_score:exercise_users[i].subjective_score === null ? "--" : exercise_users[i].subjective_score === "" ? "--" : exercise_users[i].subjective_score, operating: "评阅", commit_method:exercise_users[i].commit_method, }) @@ -1515,6 +1597,8 @@ class Studentshavecompletedthelist extends Component { completion: exercise_users[i].objective_score === undefined ? "--" : exercise_users[i].objective_score === null ? "--" : exercise_users[i].objective_score === "" ? "--" : exercise_users[i].objective_score, levelscore: exercise_users[i].subjective_score === undefined ? "--" : exercise_users[i].subjective_score === null ? "--" : exercise_users[i].subjective_score === "" ? "--" : exercise_users[i].subjective_score, efficiencyscore: exercise_users[i].score === null ? "--" : exercise_users[i].score === "" ? "--" : exercise_users[i].score, + objective_score: exercise_users[i].objective_score === null ? "--" : exercise_users[i].objective_score === "" ? "--" : exercise_users[i].objective_score, + subjective_score:exercise_users[i].subjective_score === null ? "--" : exercise_users[i].subjective_score === "" ? "--" : exercise_users[i].subjective_score, operating: "--", commit_method:exercise_users[i].commit_method }) @@ -1540,6 +1624,8 @@ class Studentshavecompletedthelist extends Component { submitstate: current_answer_user.commit_status === 1 ? "已提交" : "未提交", updatetime: moment(current_answer_user.end_at).format('YYYY-MM-DD HH:mm') === "Invalid date" ? "--" : moment(current_answer_user.end_at).format('YYYY-MM-DD HH:mm'), efficiencyscore: current_answer_user.score === undefined ? "--" : current_answer_user.score === null ? "--" : current_answer_user.score === "" ? "--" : current_answer_user.score, + objective_score: exercise_users[i].objective_score === null ? "--" : exercise_users[i].objective_score === "" ? "--" : exercise_users[i].objective_score, + subjective_score:exercise_users[i].subjective_score === null ? "--" : exercise_users[i].subjective_score === "" ? "--" : exercise_users[i].subjective_score, review_status: current_answer_user.review_status, completion: current_answer_user.objective_score === undefined ? "--" : current_answer_user.objective_score === null ? "--" : current_answer_user.objective_score === "" ? "--" : current_answer_user.objective_score, levelscore: current_answer_user.subjective_score === undefined ? "--" : current_answer_user.subjective_score === null ? "--" : current_answer_user.subjective_score === "" ? "--" : current_answer_user.subjective_score, @@ -1582,6 +1668,8 @@ class Studentshavecompletedthelist extends Component { completion: exercise_users[i].objective_score === undefined ? "--" : exercise_users[i].objective_score === null ? "--" : exercise_users[i].objective_score === "" ? "--" : exercise_users[i].objective_score, levelscore: exercise_users[i].subjective_score === undefined ? "--" : exercise_users[i].subjective_score === null ? "--" : exercise_users[i].subjective_score === "" ? "--" : exercise_users[i].subjective_score, efficiencyscore: exercise_users[i].score === null ? "--" : exercise_users[i].score === "" ? "--" : exercise_users[i].score, + objective_score: exercise_users[i].objective_score === null ? "--" : exercise_users[i].objective_score === "" ? "--" : exercise_users[i].objective_score, + subjective_score:exercise_users[i].subjective_score === null ? "--" : exercise_users[i].subjective_score === "" ? "--" : exercise_users[i].subjective_score, operating: "查看", commit_method:exercise_users[i].commit_method, }) @@ -1727,6 +1815,8 @@ class Studentshavecompletedthelist extends Component { completion: exercise_users[i].objective_score === undefined ? "--" : exercise_users[i].objective_score === null ? "--" : exercise_users[i].objective_score === "" ? "--" : exercise_users[i].objective_score, levelscore: exercise_users[i].subjective_score === undefined ? "--" : exercise_users[i].subjective_score === null ? "--" : exercise_users[i].subjective_score === "" ? "--" : exercise_users[i].subjective_score, efficiencyscore: exercise_users[i].score === undefined ? "--" : exercise_users[i].score === null ? "--" : exercise_users[i].score === "" ? "--" : exercise_users[i].score, + objective_score: exercise_users[i].objective_score === null ? "--" : exercise_users[i].objective_score === "" ? "--" : exercise_users[i].objective_score, + subjective_score:exercise_users[i].subjective_score === null ? "--" : exercise_users[i].subjective_score === "" ? "--" : exercise_users[i].subjective_score, finalscore: "评阅", user_id: exercise_users[i].user_id, commit_method:exercise_users[i].commit_method @@ -1744,6 +1834,8 @@ class Studentshavecompletedthelist extends Component { completion: exercise_users[i].objective_score === undefined ? "--" : exercise_users[i].objective_score === null ? "--" : exercise_users[i].objective_score === "" ? "--" : exercise_users[i].objective_score, levelscore: exercise_users[i].subjective_score === undefined ? "--" : exercise_users[i].subjective_score === null ? "--" : exercise_users[i].subjective_score === "" ? "--" : exercise_users[i].subjective_score, efficiencyscore: exercise_users[i].score === undefined ? "--" : exercise_users[i].score === null ? "--" : exercise_users[i].score === "" ? "--" : exercise_users[i].score, + objective_score: exercise_users[i].objective_score === null ? "--" : exercise_users[i].objective_score === "" ? "--" : exercise_users[i].objective_score, + subjective_score:exercise_users[i].subjective_score === null ? "--" : exercise_users[i].subjective_score === "" ? "--" : exercise_users[i].subjective_score, finalscore: "--", user_id: exercise_users[i].user_id, commit_method:exercise_users[i].commit_method diff --git a/public/react/src/modules/courses/exercise/new/NullChildEditor.js b/public/react/src/modules/courses/exercise/new/NullChildEditor.js index 5146f05ec..d2f710e55 100644 --- a/public/react/src/modules/courses/exercise/new/NullChildEditor.js +++ b/public/react/src/modules/courses/exercise/new/NullChildEditor.js @@ -19,11 +19,11 @@ const { Option } = Select; class NullChildEditor extends Component{ constructor(props){ super(props); - + this.state = { } } - + // toMDMode = (that) => { // if (this.mdReactObject) { // let mdReactObject = this.mdReactObject; @@ -41,7 +41,7 @@ class NullChildEditor extends Component{ render() { let { question_title, question_score, question_type, question_choices, standard_answers } = this.state; - let { question_id, index, onAnswerChange, addChildAnswer, toMDMode, exerciseIsPublish, + let { question_id, index, onAnswerChange, addChildAnswer, toMDMode, exerciseIsPublish, answers } = this.props; // marginTop: '18px' return( @@ -57,18 +57,24 @@ class NullChildEditor extends Component{ className={'nullChildEditor'} placeholder={`请输入参考答案${itemIndex == 0 ?'':'(可选)'}`} toMDMode={toMDMode} noStorage={true} - mdID={`answer_${index}${itemIndex}`} height={155} + mdID={`answer_${index}${itemIndex}`} height={155} initValue={item} onChange={(val) => onAnswerChange(index, itemIndex, val)} > - {!exerciseIsPublish && + {!exerciseIsPublish && {itemIndex != 0 && - this.props.deleteChildAnswer(index, itemIndex)} style={{float: 'right'}} > } + {itemIndex === 0 && + this.props.deleteChildAnswermain(index, itemIndex)} + style={{float: 'right'}} + > + } { addChildAnswer(index)} diff --git a/public/react/src/modules/courses/exercise/new/NullEditor.js b/public/react/src/modules/courses/exercise/new/NullEditor.js index dcb310396..9b5dcf9ef 100644 --- a/public/react/src/modules/courses/exercise/new/NullEditor.js +++ b/public/react/src/modules/courses/exercise/new/NullEditor.js @@ -38,7 +38,7 @@ class NullEditor extends Component{ answers.answer_text.forEach((item, itemIndex) => { _standard_answers[index].push(item) }) - + }) } this.state = { @@ -53,7 +53,7 @@ class NullEditor extends Component{ this.setState({ is_ordered: e.target.checked}) } - + onSave = () => { const {question_title, question_score, question_type, question_choices, standard_answers, is_ordered } = this.state; const { question_id_to_insert_after, question_id } = this.props @@ -78,8 +78,8 @@ class NullEditor extends Component{ this.props.showNotification('分值:必须大于0'); return; } else if(!question_score || intScore == NaN) { this.props.showNotification('分值:不能为空'); return; - } - + } + let isEmpty = false; standard_answers.forEach((answers, index) => { @@ -91,8 +91,8 @@ class NullEditor extends Component{ answerArray[index].answer_text.push(item) if(!item) { this.refs[`nullChildEditor${index}`].showError(itemIndex) - // this.props.showNotification(`请先输入第${index+1}个填空的第${itemIndex+1}参考答案。`); - this.props.showNotification(`答案:不能为空`); + // this.props.showNotification(`请先输入第${index+1}个填空的第${itemIndex+1}参考答案。`); + this.props.showNotification(`答案:不能为空`); isEmpty = true; } }) @@ -122,7 +122,7 @@ class NullEditor extends Component{ "is_ordered":true } }*/ - const Id = this.props.match.params.Id + const Id = this.props.match.params.Id if (question_id) { const editUrl = this.props.getEditQuestionUrl(question_id); axios.put(editUrl, { @@ -146,7 +146,7 @@ class NullEditor extends Component{ }); } else { const url = this.props.getAddQuestionUrl(); - + axios.post(url, { exercise_bank_id: Id, question_title, @@ -165,13 +165,13 @@ class NullEditor extends Component{ .catch(function (error) { console.log(error); }); - } + } } onCancel = () => { this.props.onEditorCancel() } componentDidMount = () => { - + } on_question_score_change = (e) => { this.setState({ question_score: e }) @@ -195,13 +195,13 @@ class NullEditor extends Component{ this.mdReactObject.toShowMode() } }) - + } - + onAnswerChange = (index, itemIndex, val) => { if (this.state.standard_answers[index]) { this.setState( - (prevState) => ({ + (prevState) => ({ standard_answers : update(prevState.standard_answers , {[index]: {$splice: [[itemIndex, 1, val]]}}), }) @@ -217,6 +217,21 @@ class NullEditor extends Component{ ) } + deleteChildAnswermain=(index, childIndex)=>{ + let newstandard_answers=this.state.standard_answers + + this.props.confirm({ + content: `确认要删除这个参考答案吗?`, + onOk: () => { + newstandard_answers.splice(index,1) + this.setState({ + standard_answers:newstandard_answers + }) + + } + }) + } + deleteChildAnswer = (index, childIndex) => { if(!this.state.standard_answers[index][childIndex]) { this.setState( @@ -258,16 +273,16 @@ class NullEditor extends Component{ } this.mdReactObject = that; } - + render() { let { question_title, question_score, question_type, question_choices, standard_answers , is_ordered } = this.state; let { question_id, index, exerciseIsPublish, - // question_title, - // question_type, + // question_title, + // question_type, // question_score, isNew } = this.props; - + // const { getFieldDecorator } = this.props.form; const isAdmin = this.props.isAdmin() @@ -302,17 +317,17 @@ class NullEditor extends Component{ (客观题,由系统自动评分,允许手动调分,请设置标准答案 ;支持最多5个空,每空得分按照本题的总分平均计算)

- this.setState({ question_title: val})} onPlaceholderChange={this.onPlaceholderChange} showNullButton={exerciseIsPublish ? false : true} ref="titleEditor" > - +
{ standard_answers.map((answers, index) => { - return // answer.map((item, itemIndex) => { // return
@@ -346,16 +362,16 @@ class NullEditor extends Component{ 分 - - + + 取消 保存 - +
- +
) } diff --git a/public/react/src/modules/courses/exercise/question/fillEmpty.js b/public/react/src/modules/courses/exercise/question/fillEmpty.js index 737fef682..10b906fad 100644 --- a/public/react/src/modules/courses/exercise/question/fillEmpty.js +++ b/public/react/src/modules/courses/exercise/question/fillEmpty.js @@ -44,13 +44,13 @@ class fillEmpty extends Component{ } onOptionContentChange = (value, index) => { - let array = this.state.array.slice(0); + let array = this.state.array.slice(0); array[index].value = value; this.setState({ array }) } onBlurEmpty=(index,number)=>{ - let array = this.state.array.slice(0); + let array = this.state.array.slice(0); let v=array[index].value; let question_id=this.props.questionType.question_id; let url=`/exercise_questions/${question_id}/exercise_answers.json` @@ -73,8 +73,8 @@ class fillEmpty extends Component{ } render(){ - let { - questionType , + let { + questionType , exercise , user_exercise_status, }=this.props @@ -89,11 +89,11 @@ class fillEmpty extends Component{ .emptyPanel div#content_editorMd_show{ width: 100%; border-radius: 4px; - height: 35px; + // height: 35px; margin-top:0px; background-color:#fafafa; color:#999; - line-height:25px; + // line-height:25px; } .answerStyle{ background:#f5f5f5; @@ -111,10 +111,18 @@ class fillEmpty extends Component{ 答案(填空{key+1}):
{ - user_exercise_status == 1 ? - + user_exercise_status == 1 ? + // +
+ +
: -

参考答案:

{ questionType.standard_answer && questionType.standard_answer.map((item,k)=>{ @@ -141,7 +149,7 @@ class fillEmpty extends Component{ item.answer_text && item.answer_text.map((i,index)=>{ return( //
) @@ -154,7 +162,7 @@ class fillEmpty extends Component{ }
} - + ) } diff --git a/public/react/src/modules/courses/new/CoursesNew.js b/public/react/src/modules/courses/new/CoursesNew.js index 27fcb5c1e..12f4341e0 100644 --- a/public/react/src/modules/courses/new/CoursesNew.js +++ b/public/react/src/modules/courses/new/CoursesNew.js @@ -933,7 +933,7 @@ class CoursesNew extends Component { > {getFieldDecorator("checkboxgroup", { initialValue: [ - "shixun_homework", "common_homework", "group_homework", "exercise", "attachment", "course_group","video" + "shixun_homework", "common_homework", "group_homework", "exercise", "attachment", "course_group","video","attendance" ], })( @@ -949,6 +949,7 @@ class CoursesNew extends Component { 讨论 分班 统计 + 签到 )} diff --git a/public/react/src/modules/courses/new/Goldsubject.js b/public/react/src/modules/courses/new/Goldsubject.js index 6d637552d..3cd707284 100644 --- a/public/react/src/modules/courses/new/Goldsubject.js +++ b/public/react/src/modules/courses/new/Goldsubject.js @@ -989,7 +989,7 @@ class Goldsubject extends Component { {getFieldDecorator("checkboxgroup", { initialValue: [ - "announcement","online_learning","shixun_homework","common_homework", + "announcement","online_learning","shixun_homework","common_homework","attendance" ], })( @@ -1003,6 +1003,7 @@ class Goldsubject extends Component { 讨论 分班 统计 + 签到 )} diff --git a/public/react/src/modules/courses/poll/PollDetailTabForthRules.js b/public/react/src/modules/courses/poll/PollDetailTabForthRules.js index 707c757fd..5e912524c 100644 --- a/public/react/src/modules/courses/poll/PollDetailTabForthRules.js +++ b/public/react/src/modules/courses/poll/PollDetailTabForthRules.js @@ -353,7 +353,7 @@ class PollDetailTabForthRules extends Component{ render(){ let {rules,course_group,flagPageEdit}=this.state let isAdmin=this.props.isAdmin(); - console.log(flagPageEdit) + // console.log(flagPageEdit) return(

diff --git a/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js b/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js index d83f51c0a..cd8061906 100644 --- a/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js +++ b/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js @@ -461,7 +461,7 @@ class Listofworksstudentone extends Component { ) }, { - title: '当前成绩', + title:'当前成绩', dataIndex: 'work_score', key: 'work_score', align: "center", @@ -856,7 +856,7 @@ class Listofworksstudentone extends Component { ) }, { - title: '当前成绩', + title:'当前成绩', dataIndex: 'work_score', key: 'work_score', align: "center", @@ -1061,14 +1061,14 @@ class Listofworksstudentone extends Component { }> {record.cost_time === null ? "--" : record.cost_time === undefined ? "--" : record.cost_time === "--" ? "--" : - {record.cost_time === null ? "--" : record.cost_time === undefined ? "--" : record.cost_time} - + } // 当前成绩 + 鼠标停留具体分值上可查
+ 看得分明细
+ }>
, dataIndex: 'work_score', key: 'work_score', align: 'center', @@ -1300,7 +1303,7 @@ class Listofworksstudentone extends Component { // onClick={() => this.Viewstudenttraininginformationt(record)} >{record.has_comment===true?"详情":"评阅 "}
: - this.Viewstudenttraininginformationtysl2(e, record)} // onClick={() => this.Viewstudenttraininginformationt(record)} >{record.has_comment===true?"详情":"评阅 "} @@ -1446,14 +1449,14 @@ class Listofworksstudentone extends Component { } }> {record.cost_time === null ? "--" : record.cost_time === undefined ? "--" : record.cost_time === "--" ? "--" : - {record.cost_time === null ? "--" : record.cost_time === undefined ? "--" : record.cost_time} - + } ) @@ -1569,7 +1572,10 @@ class Listofworksstudentone extends Component { ) }, { - title: '当前成绩', + title: 当前成绩 + 鼠标停留具体分值上可查
+ 看得分明细
+ }>
, dataIndex: 'work_score', key: 'work_score', align: 'center', diff --git a/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js b/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js index a6cefb466..e14ad1408 100644 --- a/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js +++ b/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js @@ -135,6 +135,8 @@ class Trainingjobsetting extends Component { borredszfls:'', total_scoretwo:0, total_score:0, + immediatelyopen:false, + modallate_time:undefined } // console.log("获取到的值") // console.log("Trainingjobsetting") @@ -1757,7 +1759,9 @@ class Trainingjobsetting extends Component { Savesname: "立即发布", Cancel: this.homeworkhide, Saves: this.homeworkstartend, - course_groups: response.data.course_groups, + course_groups:response.data.course_groups, + immediatelyopen:true, + modallate_time:response.data.late_time, }) } }).catch((error) => { @@ -1799,6 +1803,7 @@ class Trainingjobsetting extends Component { } // 立即发布 homeworkstartend = (ds, endtime) => { + debugger var homeworkid = this.props.match.params.homeworkid; let data = {} @@ -1914,6 +1919,8 @@ class Trainingjobsetting extends Component { typs: undefined, starttimes: undefined, starttimesend: undefined, + immediatelyopen:false, + modallate_time:undefined, }) } cancelBox = () => { @@ -2401,6 +2408,8 @@ class Trainingjobsetting extends Component { starttimes={this.state.starttimes} starttimesend={this.state.starttimesend} typs={this.state.typs} + immediatelyopen={this.state.immediatelyopen} + modallate_time={this.state.modallate_time} /> : ""} { + + const coursesId=this.props.match.params.coursesId; + let url=`/weapps/courses/${coursesId}/attendances.json` + axios.get(url,{params:{ + group_id:group_id==="全部"?undefined:group_id + } + }).then((response) => { + + if(response){ + if(response.data){ + let newlists=[] + + if(response.data.history_attendances.length>0){ + response.data.history_attendances.map((item,key)=>{ + newlists.push({ + month: item.index, + name: item.name+" "+item.attendance_date+" "+item.start_time+"-"+item.end_time, + // month:item.name, + city:"到课率", + temperature: (item.normal_rate*100).toFixed(0) + }) + newlists.push({ + month: item.index, + name: item.name+" "+item.attendance_date+" "+item.start_time+"-"+item.end_time, + // month:item.name, + city: "旷课率", + temperature: (item.absence_rate*100).toFixed(0) + }) + newlists.push({ + month: item.index, + name: item.name+" "+item.attendance_date+" "+item.start_time+"-"+item.end_time, + // month:item.name, + city: "请假率", + temperature: (item.leave_rate*100).toFixed(0) + }) + }) + } + + this.setState({ + datas:response.data, + newlist:newlists + }) + + + } + } + this.setState({ + spal:false + }) + }).catch((error) => { + this.setState({ + spal:false + }) + + }) + } + + componentDidMount() { + this.setState({ + spal:true + }) + const coursesId=this.props.match.params.coursesId; + let newurl=`/courses/${coursesId}/all_course_groups.json`; + axios.get(newurl).then((response) => { + let newlist=this.state.course_groups; + response.data.course_groups.map((item,key)=>{ + newlist.push(item) + }) + this.setState({ + course_groups:newlist + }) + }) + + this.getdata() + } + + handleChange=(value)=>{ + this.getdata(value) + } + + render() { + let {datas,newlist,course_groups,spal}=this.state; + + const cols = { + month: { + type: 'pow', + // nice: true, + exponent:1, + // minLimit:1, + // tickInterval:1, + // minTickInterval:2 + }, + temperature:{ + type: 'linear', + nice:[0,100], + minTickInterval:2 + } + }; + + + return( + +

+ { + spal===true? +
+ +
+ + : +
+ + + + + +
{datas&&datas.all_history_count}
+
+ + + +
{datas?(datas&&datas.avg_normal_rate*100).toFixed(0)+"%":""}
+
+ + + +
{datas?(datas&&datas.avg_absence_rate*100).toFixed(0)+"%":""}
+
+ + + +
{datas?(datas&&datas.avg_leave_rate*100).toFixed(0)+"%":""}
+
+ +
+ + {newlist&&newlist.length>0?
+
+ + + + + + + 到课率 + + + + + + 旷课率 + + + + + + 请假率 + + + + + + 显示最近十次签到 + + + + + +
+ +
+ + {/**/} + + `${val}%` + }} + /> + + { + return { + //自定义 tooltip 上显示的 title 显示内容等。 + name: city, + title: name, + value: temperature+"%" + }; + }]} + /> + { + // return { + // //自定义 tooltip 上显示的 title 显示内容等。 + // name: 'sold1', + // title: 'dddd' + month, + // value: temperature + // }; + // }]} + /> + +
+ +
:
+ +
} +
+ } +
+ + + + ) + } + +} + + + + + + +export default Signinstatistics; diff --git a/public/react/src/modules/courses/signin/component/Detailss.js b/public/react/src/modules/courses/signin/component/Detailss.js new file mode 100644 index 000000000..c1f43e0cf --- /dev/null +++ b/public/react/src/modules/courses/signin/component/Detailss.js @@ -0,0 +1,197 @@ +import React, {Component} from "react"; +import '../css/signincdi.css'; +import {Progress, message} from 'antd'; +import QRCode from 'qrcode.react'; +import axios from 'axios'; + +function clearSlct() { + if ("getSelection" in window) { + window.getSelection().removeAllRanges(); + } else { + document.selection.empty(); + } + ; +} + +function jsCopy(s) { + clearSlct(); + const copyEle = document.getElementById(s); + copyEle.select(); + const copyStatus = document.execCommand("Copy"); + // 对成功与否定进行提示 + copyStatuss(copyStatus) +} + +function copyStatuss(copyStatus) { + if (copyStatus) { + message.success('复制成功'); + } else { + message.error('复制失败'); + } +} + +//条目 +class Detailss extends Component { + //条目组件 + constructor(props) { + super(props); + + this.state = {} + } + + componentDidMount() { + + } + + componentDidUpdate = (prevProps) => { + + + } + + + render() { + const isAdmin = this.props.isAdmin(); + let item = this.props.headdata; + let defaultActiveKey = this.props.defaultActiveKey; + let jdt; + try { + jdt = item.normal_count / item.all_count * 100; + } catch (e) { + jdt = 100; + } + return ( + +
+
+

+ {item && item.name} +

+ +
+
+ 签到人数: +
+
+ +
+
+ 已签到 {item && item.normal_count ? item.normal_count : 0} / + 应签到 {item && item.all_count ? item.all_count : 0} +
+
+ + +
+
+ + +
+ 签到发起人: +
+
+ {item && item.author.user_name} +
+ +
+ 签到方式: +
+
+ {item && item.mode ? + item.mode === "QRCODE" ? + "二维码签到" + : item.mode === "NUMBER" ? + "数字签到" + : item.mode === "QUICK" ? + "快捷签到" + : "" + : ""} +
+ +
+ 开始结束时间: +
+
+ {item && item.attendance_date} {item && item.start_time}-{item && item.end_time} +
+
+ { + defaultActiveKey === "1" ? +
+ { + item && item.mode === "NUMBER" ? +
+

{ + jsCopy("file_path" + 1) + }}> + 复制签到码 +

+

+ {item&&item.code} +

+

签到码:

+
+ +
+
+ : + item && item.mode === "QRCODE" ? +
+ +
+ 扫码签到 +
+
+ + : "" + } + + +
+ + : + "" + } + +
+
+ +
+ +
+ ) + } +} + +export default Detailss; diff --git a/public/react/src/modules/courses/signin/component/NoneDatas.js b/public/react/src/modules/courses/signin/component/NoneDatas.js new file mode 100644 index 000000000..c36cc29d1 --- /dev/null +++ b/public/react/src/modules/courses/signin/component/NoneDatas.js @@ -0,0 +1,36 @@ +import React, { Component } from 'react'; +import { getImageUrl , getUrl } from 'educoder'; + +class NoneDatas extends Component{ + constructor(props) { + super(props) + } + render(){ + const { style } = this.props; + return( +
+ + +

暂无相关数据

+
+ ) + } +} +export default NoneDatas; diff --git a/public/react/src/modules/courses/signin/component/Teacherentry.js b/public/react/src/modules/courses/signin/component/Teacherentry.js new file mode 100644 index 000000000..ff08fe126 --- /dev/null +++ b/public/react/src/modules/courses/signin/component/Teacherentry.js @@ -0,0 +1,174 @@ +import React, {Component} from "react"; +import '../css/signincdi.css'; +import {Progress} from 'antd'; +import axios from 'axios'; + +//条目 +class Teacherentry extends Component { + //条目组件 + constructor(props) { + super(props); + + this.state = {} + } + + componentDidMount() { + + } + + componentDidUpdate = (prevProps) => { + + + } + + + render() { + let isAdmin = this.props.isAdmin(); + let item = this.props.item; + let index =this.props.index; + let jdt; + try { + jdt = item.normal_count / item.all_count * 100; + } catch (e) { + jdt = 100; + } + return ( + +
+
{e.stopPropagation();this.props.qiandaoxiangq(true,item.id)}:""}> +

+ { + item.name + } + +

+ +
+
+ 签到人数: +
+
+ +
+
+ 已签到 {item.normal_count ? item.normal_count : 0} / 应签到 {item.all_count ? item.all_count : 0} +
+
+ + +
+
+ + +
+ 签到发起人: +
+
+ {item.author.user_name} +
+ +
+ 签到方式: +
+
+ {item.mode ? + item.mode === "QRCODE" ? + "二维码签到" + : item.mode === "NUMBER" ? + "数字签到" + : item.mode === "QUICK" ? + "快捷签到" + : "" + : ""} +
+ +
+ 开始结束时间: +
+
+ {item.attendance_date} {item.start_time}-{item.end_time} +
+
+ +
+ { + isAdmin === true ? + this.props.defaultActiveKey === "1" ? +
+
{e.stopPropagation();this.props.thisEnd(item.id)}}>截止
+
{e.stopPropagation();this.props.thisdelete(item.id)}}>删除
+
+ : + item.edit_auth === true ? +
+
{e.stopPropagation();this.props.Signinnamestypes(item.id,true,item.name)}}>编辑
+
{e.stopPropagation();this.props.thisdelete(item.id)}}>删除
+
+ : +
+
{e.stopPropagation();this.props.thisdelete(item.id)}}>删除
+
+ : + ( + this.props.defaultActiveKey === "1" ? +
+ { + item.attendance_status? + ( + item.attendance_status==="ABSENCE"? +
{e.stopPropagation();this.props.Signin(item.mode,item.id,item.attendance_code)}}> + 签到 +
+ : + item.attendance_status==="NORMAL"? +
+ 正常签到 +
+ :"" + ) + : + "" + } + +
+ : +
+ { + item.attendance_status? + item.attendance_status === "NORMAL" ? +
+ 正常签到 +
+ : item.attendance_status === "LEAVE" ? +
+ 请假 +
+ : item.attendance_status === "ABSENCE" ? +
+ 旷课 +
+ : + "" + : + "" + } + + + + +
+ ) + + } +
+ +
+
+
+ +
+ ) + } +} + +export default Teacherentry; diff --git a/public/react/src/modules/courses/signin/component/teachercomponent/Histeacomponent.js b/public/react/src/modules/courses/signin/component/teachercomponent/Histeacomponent.js new file mode 100644 index 000000000..c5b2062f5 --- /dev/null +++ b/public/react/src/modules/courses/signin/component/teachercomponent/Histeacomponent.js @@ -0,0 +1,33 @@ +import React,{ Component } from "react"; +import '../../css/signincdi.css'; +//在线学习 +class Histeacomponent extends Component{ + //教师历史签到 + constructor(props){ + super(props); + + this.state={ + + } + } + + componentDidMount() { + + } + + componentDidUpdate = (prevProps) => { + + + } + + + + render(){ + return( + + + + ) + } +} +export default Histeacomponent; diff --git a/public/react/src/modules/courses/signin/component/teachercomponent/Signteacomponent.js b/public/react/src/modules/courses/signin/component/teachercomponent/Signteacomponent.js new file mode 100644 index 000000000..2507c6d1e --- /dev/null +++ b/public/react/src/modules/courses/signin/component/teachercomponent/Signteacomponent.js @@ -0,0 +1,33 @@ +import React,{ Component } from "react"; +import '../../css/signincdi.css'; +//在线学习 +class Signteacomponent extends Component{ + //教师签到统计 + constructor(props){ + super(props); + + this.state={ + + } + } + + componentDidMount() { + + } + + componentDidUpdate = (prevProps) => { + + + } + + + + render(){ + return( + + + + ) + } +} +export default Signteacomponent; diff --git a/public/react/src/modules/courses/signin/component/teachercomponent/Teaccomponent.js b/public/react/src/modules/courses/signin/component/teachercomponent/Teaccomponent.js new file mode 100644 index 000000000..099625aaf --- /dev/null +++ b/public/react/src/modules/courses/signin/component/teachercomponent/Teaccomponent.js @@ -0,0 +1,200 @@ +import React,{ Component } from "react"; +import '../../css/signincdi.css'; +import axios from 'axios'; +import Teacherentry from "../Teacherentry"; +import NoneDatas from '../NoneDatas'; +import LoadingSpin from '../../../../../common/LoadingSpin'; +import { Modal, Button } from 'antd'; + +const { confirm } = Modal; + +//在线学习 +class Teaccomponent extends Component{ + //教师正在签到 + constructor(props){ + super(props); + + this.state={ + mydatas:[] + } + } + + componentDidMount() { + this.setState({ + mydatas:this.props.datas + }) + + } + + + + + componentDidUpdate = (prevProps) => { + + + } + //截止 + thisEnd=(attendance_id)=>{ + let thiss=this + + confirm({ + title: '提示', + content: '确认截止此签到吗?', + onOk() { + const url = `/weapps/attendances/${attendance_id}/end.json`; + var data={ + + } + axios.post(url, data) + .then((result) => { + if (result.data.status === 0) { + // try { + // this.props.showNotification(`截止成功`); + // }catch (e) { + // + // } + thiss.props.getsetdatas(); + }else{ + thiss.props.showNotification(result.data.message); + + } + }).catch((error) => { + //console.log(error); + }) + }, + onCancel() { + + }, + }); + + } + //删除 + thisdelete=(attendance_id)=>{ + let thiss=this + confirm({ + title: '提示', + content: '确认删除此签到吗?', + onOk () { + const url = `/weapps/attendances/${attendance_id}.json`; + var data={ + + } + axios.delete(url, data) + .then((result) => { + if (result.data.status === 0) { + // try { + // thiss.props.showNotification(`删除成功`); + // }catch (e) { + // + // } + + thiss.props.getsetdatas(); + }else{ + thiss.props.showNotification(result.data.message); + + } + }).catch((error) => { + //console.log(error); + }) + }, + onCancel() { + + }, + }); + + } + + //学生签到 + Signin=(attendance_mode,attendance_id,code)=>{ + + const url = `/weapps/course_member_attendances.json`; + var data={ + + } + if(attendance_mode==="QUICK"){ + //快捷签到 + data={ + attendance_mode:"QUICK", + attendance_id:attendance_id, + } + axios.post(url, data) + .then((result) => { + if (result.data.status === 0) { + try { + this.props.showNotification(`签到成功`); + }catch (e) { + + } + + this.props.getsetdatas(); + }else{ + this.props.showNotification(result.data.message); + + } + }).catch((error) => { + }) + }else if(attendance_mode==="QRCODE"){ + //二维码签到 + data={ + attendance_mode:"QRCODE", + attendance_id:attendance_id, + code:code, + } + this.props.GotomQrcodesodesy(data,true) + }else if(attendance_mode==="NUMBER"){ + //数字签到 + data={ + attendance_mode:"NUMBER", + attendance_id:attendance_id, + code:code, + } + this.props.Gotomodes(data,true) + + } + + + } + + + + render(){ + let mydatas=this.props.datas?this.props.datas:[]; + let Spin= this.props.Spin; + return( + +
+ { + Spin&&Spin===true? + + : + + ( + mydatas&&mydatas.length>0? + mydatas.map((object, index) => { + return ( + this.props.Signinnamestypes(id,b,a)} + Signin={(a,b,c)=>this.Signin(a,b,c)} + qiandaoxiangq={(b,id)=>this.props.qiandaoxiangq(b,id)} + thisdelete={(id)=>this.thisdelete(id)} + thisEnd={(id)=>this.thisEnd(id)} + item={object} key={index} + index={index} + defaultActiveKey={this.props.defaultActiveKey} + {...this.props} + {...this.state} + > + + ) + }) + : + + ) + + } +
+
+ ) + } +} +export default Teaccomponent; diff --git a/public/react/src/modules/courses/signin/css/Signinstatistics.css b/public/react/src/modules/courses/signin/css/Signinstatistics.css new file mode 100644 index 000000000..c7ff88c9f --- /dev/null +++ b/public/react/src/modules/courses/signin/css/Signinstatistics.css @@ -0,0 +1,233 @@ +.gutterrowbox{ + height:150px; + border: none !important; +} +.gutterrowbox .ant-card-body{ + height:150px; + position: relative; +} +.gutterrowboxcontent{ + position: absolute; + bottom: 20px; + font-size:36px; + font-weight:400; + color:rgba(255,255,255,1); +} +.ml14{ + margin-left: 14px !important; +} +.ml8{ + margin-left: 8px !important; +} +.ml20{ + margin-left: 20px !important; +} +.lishiqiandao{ + + height: 150px; + + background-size: cover !important; + background-position: center !important; + background-repeat: no-repeat !important; +} +.daokeqiandao{ + + height: 150px; + + background-size: cover !important; + background-position: center !important; + background-repeat: no-repeat !important; +} +.kuangkeqiandao{ + + height: 150px; + + background-size: cover !important; + background-position: center !important; + background-repeat: no-repeat !important; +} +.qingjiaqiandao{ + + height: 150px; + + background-size: cover !important; + background-position: center !important; + background-repeat: no-repeat !important; +} + +.SigninstatisticsChart{ + height:519px; + background:rgba(255,255,255,1); + box-shadow:0px 3px 7px 0px rgba(0, 0, 0, 0.05); +} +.pd30{ + padding:30px; +} +.padding03000{ + padding: 0px 30px 0px 0px; +} + +.mindaoke{ + width: 20px; + height: 20px; + background-image: url(./dot-green@2x.png); + + background-size: cover; + background-position: center; + background-repeat: no-repeat; +} + +.minkuangke{ + width: 20px; + height: 20px; + background-image: url(./dot-orange@2x.png); + + background-size: cover; + background-position: center; + background-repeat: no-repeat; +} + +.minqingjia{ + width: 20px; + height: 20px; + background-image: url(./dot-orange@2x1.png); + + background-size: cover; + background-position: center; + background-repeat: no-repeat; +} + +.Signinstatisticsfont{ + font-weight: 400; + color: rgba(153,153,153,1); + text-align: right; + margin-right: 20px; + line-height: 32px !important; + height: 32px !important; +} +.Signinstatisticsfontselect .ant-select-selection--single .ant-select-selection__rendered{ + line-height: 32px !important; + height: 32px !important; +} + +#form_in_modal_group_ids .ant-select-selection{ + background: #fafafa !important; +} + +#form_in_modal_attendance_date .ant-calendar-picker-input { + height:40px; +} + +#form_in_modal_start_time{ + height:40px; + background: #fafafa !important; +} + +#form_in_modal_end_time{ + height:40px; + background: #fafafa !important; +} + +.Signedinlistbox .ant-select-selection--single{ + height:40px !important; + background:rgba(255,255,255,1); + border:1px solid rgba(234,234,234,1); + border-radius:4px; +} + +.Signedinlistbox .ant-select-selection__rendered{ + line-height: 40px !important; +} + +.Signedintextright{ + text-align: right; +} + +.color26C7C9{ + color:#26C7C9; +} + +.colorEAAE4E{ + color:#EAAE4E; +} + +.colorFF835C{ + color:#FF835C; +} + +.color909399{ + color:#909399; +} + +.color1890FF{ + color:#1890FF; +} + +.backfff .ant-table-thead tr th{ + background: #fff; + border-bottom:1px solid #EBEBEB !important; +} + +.textcenter{ + text-align: center !important; +} + +.sginboxcolor26C7C9 .ant-select-selection--single{ + border:1px solid #26C7C9 !important; + } + +.sginboxcolor26C7C9 .ant-select-arrow{ + color: #26C7C9 !important; +} + +.sginboxcolorEAAE4E .ant-select-selection--single{ + border:1px solid #EAAE4E !important; +} + +.sginboxcolorEAAE4E .ant-select-arrow{ + color: #EAAE4E !important; +} + +.sginboxcolorFF835C .ant-select-selection--single{ + border:1px solid #FF835C !important; +} + +.sginboxcolorFF835C .ant-select-arrow{ + color: #FF835C !important; +} + + + +.sginboxcolor909399 .ant-select-selection--single{ + border:1px solid #909399 !important; +} + +.sginboxcolor909399 .ant-select-arrow{ + color: #909399 !important; +} + +.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item { + height: auto !important; + padding: 0px !important; + white-space: nowrap !important; +} + +.color26C7C9 .ant-select-selection-selected-value{ + margin: 0 30% !important; +} + +.colorEAAE4E .ant-select-selection-selected-value{ + margin: 0 40% !important; +} + +.color909399 .ant-select-selection-selected-value{ + margin: 0 37% !important; +} + +.colorFF835C .ant-select-selection-selected-value{ + margin: 0 37% !important; +} + +/*.allSignedinlistbox .ant-select-selection-selected-value{*/ +/* margin: 0 30% !important;*/ +/*}*/ \ No newline at end of file diff --git a/public/react/src/modules/courses/signin/css/dot-green@2x.png b/public/react/src/modules/courses/signin/css/dot-green@2x.png new file mode 100644 index 000000000..a9a2cea51 Binary files /dev/null and b/public/react/src/modules/courses/signin/css/dot-green@2x.png differ diff --git a/public/react/src/modules/courses/signin/css/dot-orange@2x.png b/public/react/src/modules/courses/signin/css/dot-orange@2x.png new file mode 100644 index 000000000..0489eb573 Binary files /dev/null and b/public/react/src/modules/courses/signin/css/dot-orange@2x.png differ diff --git a/public/react/src/modules/courses/signin/css/dot-orange@2x1.png b/public/react/src/modules/courses/signin/css/dot-orange@2x1.png new file mode 100644 index 000000000..a2faaabf7 Binary files /dev/null and b/public/react/src/modules/courses/signin/css/dot-orange@2x1.png differ diff --git a/public/react/src/modules/courses/signin/css/signincdi.css b/public/react/src/modules/courses/signin/css/signincdi.css new file mode 100644 index 000000000..7ad9a0d0c --- /dev/null +++ b/public/react/src/modules/courses/signin/css/signincdi.css @@ -0,0 +1,425 @@ +/* 中间居中 */ +.intermediatecenter{ + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} +/* 简单居中 */ +.intermediatecenterysls{ + display: flex; + align-items: center; +} + +/* 头顶部居中 */ +.topcenter{ + display: -webkit-flex; + flex-direction: column; + align-items: center; + +} +/* 均匀分开 */ +.spacearound{ + display: flex; + justify-content: space-around; + +} +/* 两边靠墙中间均匀分开 */ +.spacebetween{ + display: flex; + justify-content: space-between; +} +/* 从左分开 */ +.spacearoundflexstart{ + + display: flex; + justify-content: flex-start; +} +.spacebetween{ + display: flex; + justify-content: space-between; +} + +.unpsysls{ + display: inline-block; + text-align: justify; + font-size: 28px; +} + +/* x轴正方向排序 */ +/* 一 二 三 四 五 六 七 八 */ +.sortinxdirection{ + display: flex; + flex-direction:row; +} +/* x轴反方向排序 */ +/* 八 七 六 五 四 三 二 一 */ +.xaxisreverseorder{ + display: flex; + flex-direction:row-reverse; +} +/* 垂直布局 正方向*/ +/* 一 + 二 + 三 + 四 + 五 + 六 + 七 + 八 */ +.verticallayout{ + display: flex; + flex-direction:column; +} +/* 垂直布局 反方向*/ +.reversedirection{ + display: flex; + flex-direction:column-reverse; +} +/* 两端对齐 */ +.alignmentatbothends{ + display: flex; + justify-content: space-between; +} +.file_path_input{ + position: absolute; + right: -50%; +} +.ws100s{ + width: 100%; +} + +.ws70s{ + width: 70%; + } + +.ws75s{ + width: 75%; +} +.ws80s{ + width: 80%; +} +.ws50s{ + width: 50%; +} +.hs30s{ + height: 30%; +} +.ws20s{ + width: 20%; +} +.ws25s{ + width: 25%; +} +.yslmaxheigthk{ + +} +.mysligtes{ + font-size:16px; + font-family:Microsoft YaHei; + font-weight:bold; + color:rgba(144,147,153,1); + padding-left: 34px; +} +.yslmaxheigthk .ws100s .ant-tabs .ant-tabs-bar { + border-bottom: 1px solid #ffffff !important; + background: #ffffff !important; + background-color: #ffffff !important; + margin: 0 !important; +} +.yslmaxheigthk .ws100s .ant-tabs .ant-tabs-bar .ant-tabs-nav-container { + padding-left: 25px; +} +.yslmaxheigthk .ws100s .ant-tabs .ant-tabs-bar .ant-tabs-nav-container .ant-tabs-ink-bar{ + width: 68px !important; + left: 14px; + +} + +.yslmaxheigthk .ws100s .ant-tabs .ant-tabs-bar .ant-tabs-tab{ + padding: 25px 16px !important; + font-size: 16px; + font-family: Microsoft YaHei; + font-weight: bold; + +} +.positiondivs{ + position: absolute; + top: 0; + right: 0; +} +.posiivs{ + padding: 30px 5px !important; + font-size: 16px; + font-family: Microsoft YaHei; + font-weight: bold; + color: #1890ff; + margin-right: 20px; +} + +.posiivsicon{ + padding: 22px 0px !important; + font-size: 16px; + font-family: Microsoft YaHei; + font-weight: bold; + color: #1890ff; + } +.posiivsiconmyss{ + font-size: 14px; + font-family: Microsoft YaHei; + color: #999999; +} +.xiaoshou{ + cursor:pointer !important; +} +.xiaoshout{ + cursor:default !important; +} + +.teacherentrydiv{ + padding-left: 32px; + padding-right: 32px; + padding-top: 20px; + padding-bottom: 20px; +} +.teacherentrydivs{ + padding-left: 25px; + padding-right: 25px; + padding-top: 20px; + padding-bottom: 20px; +} +.teachedivp{ + font-size:16px; + font-family:Microsoft YaHei; + font-weight:bold; + color:rgba(51,51,51,1); +} +.ymaxnamewidthdivp{ + max-width:100%; + width: 100%; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; + cursor: default; +} +.teachedivps{ + font-size:14px; + font-family:Microsoft YaHei; + font-weight:400; + color:rgba(144,147,153,1); +} +.teachedivpsy{ + font-size:14px; + font-family:Microsoft YaHei; + font-weight:400; + color:#333333; + +} +.progressivps{ + width:200px; + margin-left: 10px; + +} +.progressivpss{ + font-size:12px; + line-height: 23px; + font-family:Microsoft YaHei; + font-weight:400; + color:rgba(64,158,255,1); + margin-left: 15px; +} +.w60ys{ + width: 60px; + } + +.ymaxnamewidth60{ + max-width: 70px; + width: 70px; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; + cursor: default; +} +.ymaxnamewidth80{ + max-width: 80px; + width: 80px; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; + cursor: default; +} +.w80ys{ + width: 80px; +} + +.jiezhis{ + font-size:14px; + font-family:Microsoft YaHei; + font-weight:400; + color:rgba(64,158,255,1); +} +.shanchu{ + font-size:14px; + font-family:Microsoft YaHei; + font-weight:400; + color:rgba(245,108,108,1); + margin-right: 30px; +} + +.qiandaobutton{ + font-size:16px; + font-family:Microsoft YaHei; + font-weight:400; + color:rgba(255,255,255,1); + text-align: center; + line-height: 40px; + width:100px; + height:40px; + background:rgba(64,158,255,1); + border-radius:4px; +} + +.zcqiandao{ + width:100px; + height:40px; + border:1px solid rgba(38,199,201,1); + border-radius:4px; + font-size:16px; + font-family:Microsoft YaHei; + font-weight:400; + color:rgba(38,199,201,1); + text-align: center; + line-height: 40px; + +} +.qjqiandao{ + width:100px; + height:40px; + border:1px solid #EAAE4E; + border-radius:4px; + font-size:16px; + font-family:Microsoft YaHei; + font-weight:400; + color:#EAAE4E; + text-align: center; + line-height: 40px; + + +} + +.kkqiandao{ + width:100px; + height:40px; + border:1px solid #FF835C; + border-radius:4px; + font-size:16px; + font-family:Microsoft YaHei; + font-weight:400; + color:#FF835C; + text-align: center; + line-height: 40px; + +} +.h40s{ + line-height: 40px !important; +} +.h28s{ + line-height: 28px !important; +} +.mt40{ + margin-top: 40px !important; +} + +.h500{ + min-height: 500px; +} +.kkp{ + font-size:14px; + font-family:Microsoft YaHei; + font-weight:400; +} +.pr32{ + padding-right: 32px; +} + +.mr20r{ + margin-right: 20px; +} + +.color-reds{ + color:rgba(245,108,108,1) !important; +} + +.fh{ + font-size:16px; + font-family:Microsoft YaHei; + font-weight:400; + color:rgba(153,153,153,1); + +} + +.colorbluesigin{ + font-size:16px; + font-weight:bold; + color:rgba(51,51,51,1); +} +.sptits{ + font-size:20px; + font-family:MicrosoftYaHeiSemibold; + color:rgba(51,51,51,1); +} +.sptitss{ + font-size:14px; + font-family:MicrosoftYaHei; + color:rgba(136,136,136,1); + line-height:40px; +} +.tbrt{ + padding-top: 22px; + padding-left: 28px; +} +.tbrt .ts { + font-size:12px; + font-family:MicrosoftYaHei; + color:rgba(255,255,255,1); +} + +.tbrt .tss{ + font-size:30px; + font-family:MicrosoftYaHei-Bold,MicrosoftYaHei; + font-weight:bold; + color:rgba(255,255,255,1); + line-height: 33px; +} +.maxnamewidth150s{ + width: 150px; + max-width: 150px; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; + cursor: default; +} +.maxnamewidth200s{ + text-align: center; + width: 200px; + max-width:200px; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; + cursor: default; +} +.maxnamewidth100s{ + width: 100px; + max-width: 100px; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; + cursor: default; +} + +.font-14{ + font-size: 14px !important; +} +.mr32{ + margin-right: 32px; +} diff --git a/public/react/src/modules/courses/signin/model/Createsignmodel.js b/public/react/src/modules/courses/signin/model/Createsignmodel.js new file mode 100644 index 000000000..117531750 --- /dev/null +++ b/public/react/src/modules/courses/signin/model/Createsignmodel.js @@ -0,0 +1,358 @@ +import "../css/Signinstatistics.css" +import React from 'react' +import { Modal , Form, Input, Radio,DatePicker,Select,Button} from 'antd'; +import moment from 'moment'; +import axios from 'axios'; +import TimePicker from "react-datepicker"; +import "react-datepicker/dist/react-datepicker.css"; +function range(start, end) { + const result = []; + for (let i = start; i < end; i++) { + result.push(i); + } + return result; +} + +function disabledDateTime() { + return { + disabledMinutes: () => range(1, 30).concat(range(31, 60)), + // disabledSeconds: () => range(0,60) + } +} + +function disabledDate(current) { + return current && current < moment().endOf('day').subtract(1, 'days'); +} + +const CollectionCreateForm = Form.create({ name: 'form_in_modal' })( + + class extends React.Component { + constructor(props){ + super(props); + this.state = { + course_groups:[], + type:false, + dateString:null, + start_time: "", + end_time:"" + } + } + + + handleChangestart_time = (date) => { + + this.setState({ + start_time: date + }); + }; + + handleChangeend_time=(date)=>{ + + this.setState({ + end_time: date + }); + } + componentDidMount() { + const coursesId=this.props.match.params.coursesId; + let newurl=`/courses/${coursesId}/all_course_groups.json`; + axios.get(newurl).then((response) => { + + this.setState({ + course_groups:response.data.course_groups + }) + + }) + + } + + hideCreatesign=(e)=>{ + e.preventDefault(); + const {settabsdata,hideCreatesign} = this.props; + const coursesId=this.props.match.params.coursesId; + this.setState({ + type:true + }) + this.props.form.validateFields((err, values) => { + if (!err) { + if(moment(values.end_time).format('HH:mm') { + if (response.data.status == 0) { + this.props.showNotification(`创建签到成功`); + this.setState({ + type:false + }) + hideCreatesign() + settabsdata() + } + }) + .catch(function (error) { + console.log(error); + }); + }else{ + this.setState({ + type:false + }) + } + + + }); + } + // getDisabledHours=()=> { + // let hours = [] + // if(this.state.dateString===moment().format('YYYY-MM-DD')){ + // let time = moment().format('HH:mm') + // let timeArr = time.split(':') + // for (var i = 0; i < parseInt(timeArr[0]); i++) { + // hours.push(i) + // } + // } + // + // return hours + // } + // + // getDisabledMinutes=(selectedHour)=>{ + // let minutes = [] + // if(this.state.dateString===moment().format('YYYY-MM-DD')){ + // let time = moment().format('HH:mm') + // let timeArr = time.split(':') + // if (selectedHour == parseInt(timeArr[0])) { + // for(var i = 0; i < parseInt(timeArr[1]); i++) { + // minutes.push(i) + // } + // } + // } + // return minutes + // } + + onChange=(date, dateString)=>{ + this.setState({ + dateString:dateString + }) + } + render() { + const { visible, form ,setRadio,Radiolist,hideCreatesign} = this.props; + let {course_groups}=this.state; + const { getFieldDecorator } = form; + const { Option } = Select; + const formItemLayout = { + labelCol: { span: 4 }, + wrapperCol: { span: 19}, + }; + + const leftbuton={ + "width":"130px", + "height":"40px", + "border":"1px solid rgba(76,172,255,1)", + "borderRadius":"4px", + } + + const rightbuton={ + "width":"130px", + "height":"40px", + "background":"#4CACFF", + "borderRadius":"4px", + } + const format = 'HH:mm'; + + return ( + +
+ + + {getFieldDecorator('name', { + rules: [{ required: true, message: '请输入签到名称' },{whitespace:true,message: '请勿输入空格'}], + })()} + + + + {getFieldDecorator('group_ids')( + , + )} + + + + {getFieldDecorator('mode', { + rules: [{ required: true, message: '请选择签到方式' }], + })( + trigger.parentNode}> + 快捷签到 + 签到码签到 + 二维码签到 + , + )} + {Radiolist==="QUICK"?
学生点击签到按钮即可完成签到
:""} + + {Radiolist==="NUMBER"?
学生需要输入签到码才能完成签到
:""} + + {Radiolist==="QRCODE"?
学生需从小程序进入课堂扫码才能完成签到
:""} +
+ + + {getFieldDecorator('attendance_date', { + rules: [{ type: 'object', required: true, message: '请选择签到日期',}], + })( trigger.parentNode} + disabledTime={disabledDateTime} + disabledDate={disabledDate} + onChange={this.onChange} + />)} + + + + {getFieldDecorator('start_time', { + rules: [{ type: 'object', required: true, message: '请选择开始时间' }], + })( + // trigger.parentNode}/> + this.handleChangestart_time(date)} + showTimeSelect + showTimeSelectOnly + timeIntervals={10} + timeCaption="Time" + timeFormat="HH:mm" + dateFormat="HH:mm" + getPopupContainer={trigger => trigger.parentNode} + /> + )} + + + + {getFieldDecorator('end_time', { + rules: [{ type: 'object', required: true, message: '请选择结束时间' }], + })( + // trigger.parentNode} + // disabledHours={this.getDisabledHours} + // disabledMinutes={this.getDisabledMinutes} + // + // /> + this.handleChangeend_time(date)} + showTimeSelect + showTimeSelectOnly + timeIntervals={10} + minTime={this.state.dateString===moment().format('YYYY-MM-DD')||this.state.dateString===null?new Date():false} + maxTime={this.state.dateString===moment().format('YYYY-MM-DD')||this.state.dateString===null?moment().endOf('day').toDate():false} + timeCaption="Time" + timeFormat="HH:mm" + dateFormat="HH:mm" + getPopupContainer={trigger => trigger.parentNode} + /> + )} + + + + + + +
+
+ ); + } + }, +); + +class Createsignmodel extends React.Component { + state = { + Radiolist: "", + }; + setRadio=(e)=>{ + + this.setState({ + Radiolist:e.target.value + }) + } + render() { + return ( + + this.setRadio(e)} + /> + + ); + } +} + +export default Createsignmodel; + + diff --git a/public/react/src/modules/courses/signin/model/Qrcodesignin.js b/public/react/src/modules/courses/signin/model/Qrcodesignin.js new file mode 100644 index 000000000..6bc517043 --- /dev/null +++ b/public/react/src/modules/courses/signin/model/Qrcodesignin.js @@ -0,0 +1,77 @@ +import React,{ Component } from "react"; +import { Modal , Button} from 'antd'; +import QRCode from 'qrcode.react'; + +class Qrcodesignin extends Component { + constructor(props) { + super(props) + } + render() { + + const leftbuton={ + "width":"130px", + "height":"40px", + "border":"1px solid rgba(76,172,255,1)", + "borderRadius":"4px", + } + + const rightbuton={ + "width":"130px", + "height":"40px", + "background":"#4CACFF", + "borderRadius":"4px", + } + return( + + {this.props.Qrcodesignintype? + + + :""} + + + ) + } + +} + + + + + + +export default Qrcodesignin diff --git a/public/react/src/modules/courses/signin/model/Signinname.js b/public/react/src/modules/courses/signin/model/Signinname.js new file mode 100644 index 000000000..0e88e3287 --- /dev/null +++ b/public/react/src/modules/courses/signin/model/Signinname.js @@ -0,0 +1,105 @@ +import React,{ Component } from "react"; + +import { Modal , Form, Input,Button} from 'antd'; +import axios from 'axios'; + +class Signinname extends Component { + constructor(props) { + super(props) + } + setdatas=()=>{ + this.props.form.validateFields((err, values) => { + if (!err) { + const url = `/weapps/attendances/${this.props.switattendance_id}.json`; + let data={ + name:values.name, + } + axios.put(url, data) + .then((result) => { + if (result.data.status === 0) { + try { + this.props.showNotification(`修改成功`); + }catch (e) { + + } + this.props.getsetdatas(); + this.props.Signinnamestypes(null,false,"") + }else{ + this.props.showNotification(result.data.message); + + } + }).catch((error) => { + }) + } + }); + + + + } + render() { + const { getFieldDecorator } = this.props.form; + const formItemLayout = { + wrapperCol: { span: 25}, + }; + + const leftbuton={ + "width":"130px", + "height":"40px", + "border":"1px solid rgba(76,172,255,1)", + "borderRadius":"4px", + } + + const rightbuton={ + "width":"130px", + "height":"40px", + "background":"#4CACFF", + "borderRadius":"4px", + } + return( + + {this.props.Signinnamestype? + +
+ + + {getFieldDecorator('name', {initialValue: this.props.mybianjiname, + rules: [{ required: true, message: '请输入签到名称' }], + })()} + + + + + +
+
:""} +
+ + ) + } + +} + + + + +const Signinnames = Form.create({ name: 'Signinnames' })(Signinname); + +export default Signinnames diff --git a/public/react/src/modules/courses/signin/model/Studentssignmodel.js b/public/react/src/modules/courses/signin/model/Studentssignmodel.js new file mode 100644 index 000000000..0bb736c3e --- /dev/null +++ b/public/react/src/modules/courses/signin/model/Studentssignmodel.js @@ -0,0 +1,107 @@ +import React,{ Component } from "react"; + +import { Modal , Form, Input,Button} from 'antd'; +import axios from 'axios'; + +class Studentssignmodel extends Component { + constructor(props) { + super(props) + } + setdatas=()=>{ + this.props.form.validateFields((err, values) => { + if (!err) { + const url = `/weapps/course_member_attendances.json`; + let data={ + code:values.name, + attendance_mode:"NUMBER", + attendance_id:this.props.Studentssigntypedata.attendance_id, + } + axios.post(url, data) + .then((result) => { + if (result.data.status === 0) { + try { + this.props.showNotification(`签到成功`); + }catch (e) { + + } + this.props.getsetdatas(); + this.props.Gotomodes(null,false) + }else{ + this.props.showNotification(result.data.message); + + } + }).catch((error) => { + }) + } + }); + + + + } + render() { + const { getFieldDecorator } = this.props.form; + const formItemLayout = { + wrapperCol: { span: 25}, + }; + + const leftbuton={ + "width":"130px", + "height":"40px", + "border":"1px solid rgba(76,172,255,1)", + "borderRadius":"4px", + } + + const rightbuton={ + "width":"130px", + "height":"40px", + "background":"#4CACFF", + "borderRadius":"4px", + } + return( + + {this.props.Studentssigntype? + +
+ + + {getFieldDecorator('name', { + rules: [{ required: true, message: '请输入签到码' }], + })()} + + + + + +
+
:""} +
+ + ) + } + +} + + + + +const Studentssignmodels = Form.create({ name: 'Studentssignmodel' })(Studentssignmodel); + +export default Studentssignmodels diff --git a/public/react/src/modules/courses/signin/model/sigininmodes.js b/public/react/src/modules/courses/signin/model/sigininmodes.js new file mode 100644 index 000000000..e69de29bb diff --git a/public/react/src/modules/courses/signin/mymain/Signinmain.js b/public/react/src/modules/courses/signin/mymain/Signinmain.js new file mode 100644 index 000000000..6912508ba --- /dev/null +++ b/public/react/src/modules/courses/signin/mymain/Signinmain.js @@ -0,0 +1,44 @@ +import React,{ Component } from "react"; +import '../css/signincdi.css'; +import { Tabs } from 'antd'; +import Teachers_signin from '../teacher/Teachers_signin'; +import Students_signin from '../student/Signindetails'; +import axios from 'axios'; + +const { TabPane } = Tabs; +// 主签到目录 主签到目录 +class Signinmain extends Component{ + constructor(props){ + super(props); + + this.state={ + + } + } + + componentDidMount() { + + } + + componentDidUpdate = (prevProps) => { + + + } + + + // 主签到目录 + render(){ + const isAdmin = this.props.isAdmin() + + return( + +
+ + + +
+
+ ) + } +} +export default Signinmain; diff --git a/public/react/src/modules/courses/signin/student/Signedinlist.js b/public/react/src/modules/courses/signin/student/Signedinlist.js new file mode 100644 index 000000000..ea5ffb998 --- /dev/null +++ b/public/react/src/modules/courses/signin/student/Signedinlist.js @@ -0,0 +1,395 @@ +import "../css/Signinstatistics.css" +import React,{ Component } from "react"; +import { Row, Col,Select,Table,Pagination } from 'antd'; +import axios from 'axios'; +import LoadingSpin from "../../../../common/LoadingSpin"; +import NoneDatas from "../component/NoneDatas"; +const { Option } = Select; + +class Signedinlist extends Component { + + constructor(props) { + super(props) + this.state={ + limit:5, + page:1, + loading:false, + attendanceslist:[], + data:null, + group_ids:[], + members_count:0, + attendance_status:undefined, + state:[ + {id:undefined,name:"全部状态"}, + {id:"NORMAL",name:"正常签到"}, + {id:"LEAVE",name:"请 假"}, + {id:"ABSENCE",name:this.props.defaultActiveKey ==="2"?"旷 课":"未签到"}, + ], + newstate:[ + {id:"NORMAL",name:"正常签到"}, + {id:"LEAVE",name:"请 假"}, + {id:"ABSENCE",name:this.props.defaultActiveKey ==="2"?"旷 课":"未签到"}, + ], + course_members_count:'--', + attendance_count:'--', + } + } + + componentDidMount() { + this.getdatas(this.props&&this.props.switattendance_id,1,[],undefined) + // this.getpath() + } + componentDidUpdate(prevProps, prevState) { + if(prevProps.headdata!=this.props.headdata){ + + if(this.props.headdata){ + let listattendanceslist=[]; + listattendanceslist.push({id:undefined,name:"全部分班"}) + if(this.props.headdata.course_groups){ + + if(this.props.headdata.course_groups.length>0){ + this.props.headdata.course_groups.map((item,key)=>{ + listattendanceslist.push(item) + }) + } + } + this.setState({ + attendanceslist:listattendanceslist, + data:this.props.headdata, + course_members_count:this.props.headdata.normal_count, + attendance_count:this.props.headdata.all_count + }) + } + + + } + } + getdatas=(id,page,group_ids,attendance_status)=>{ + this.setState({ + loading:true, + member_attendances:[], + }) + let that=this; + let url=`/weapps/course_member_attendances.json`; + axios.get(url,{params:{ + group_ids:group_ids, + attendance_id:id, + attendance_status:attendance_status, + page:page, + limit:5, + } + }).then((response) => { + + if(response){ + this.setState({ + member_attendances:response.data.member_attendances, + members_count:response.data.members_count, + + }) + + } + this.setState({ + loading:false + }) + }).catch((error) => { + that.setState({ + loading:false + }) + + }); + } + handleChangegroup_ids=(value)=>{ + let neval + if(!value){ + neval=[] + this.setState({ + group_ids: [], + page:1 + }) + }else{ + neval=[value] + this.setState({ + group_ids: [value], + page:1 + }) + } + + let {page,attendance_status}=this.state; + this.getdatas(this.props&&this.props.switattendance_id,1,neval,attendance_status) + if(this.props.defaultActiveKey==="1"){ + this.getpath(value) + } + + } + getpath=(id)=>{ + let listattendanceslist=[] + let url=`/weapps/attendances/${this.props&&this.props.switattendance_id}.json`; + axios.get(url).then((response) => { + if(response.data){ + listattendanceslist.push({id:undefined,name:"全部分班"}) + if(response.data.course_groups.length>0){ + response.data.course_groups.map((item,key)=>{ + listattendanceslist.push(item) + }) + } + if(id){ + response.data.course_groups.map((item,key)=>{ + if(item.id===id){ + this.setState({ + course_members_count:item.course_members_count, + attendance_count: item.attendance_count + }) + } + + }) + }else{ + this.setState({ + course_members_count:response.data.normal_count, + attendance_count: response.data.all_count + }) + } + this.setState({ + attendanceslist:listattendanceslist, + data:response.data + }) + + } + + }) + } + handleChangestate=(value)=>{ + let neval + if(!value){ + neval=undefined + this.setState({ + attendance_status:undefined, + page:1 + }) + }else{ + neval=value + this.setState({ + attendance_status: value, + page:1 + }) + } + + let {page,group_ids}=this.state; + this.getdatas(this.props&&this.props.switattendance_id,1,group_ids,neval) + } + paginationonChange = (pageNumber) => { + this.setState({ + page: pageNumber, + }) + let {group_ids,attendance_status}=this.state; + this.getdatas(this.props&&this.props.switattendance_id,pageNumber,group_ids,attendance_status) + + } + + handleChange=(attendance_status,value)=>{ + + let {member_attendances}=this.state; + let newmember_attendances=member_attendances; + + newmember_attendances.map((item,key)=>{ + if(item.user_id===value){ + item.attendance_status=attendance_status + } + }) + + + let url=`/weapps/course_member_attendances/update_status.json`; + + axios.post(url, { + attendance_id:this.props.switattendance_id, + attendance_status:attendance_status, + course_id:this.props.match.params.coursesId, + user_id: value, + }) + .then((response) => { + if (response.data.status == 0) { + this.props.showNotification(`修改状态成功`); + this.setState({ + member_attendances:newmember_attendances + }) + this.props.mygetdatas() + } + }) + .catch(function (error) { + console.log(error); + }); + + + + } + render() { + let {attendanceslist,state,data,member_attendances,newstate,attendance_status}=this.state; + + const columns = [ + { + title: '序号', + dataIndex: 'index', + key: 'index', + width:300, + className: "textcenter", + }, + { + title: '姓名', + dataIndex: 'user_name', + key: 'user_name', + width:300, + className: "textcenter", + render: (text, record) => ( + {record.user_name===null?"--":record.user_name} + ) + }, + { + title: '学号', + dataIndex: 'student_id', + key: 'student_id', + width:300, + className: "textcenter", + render: (text, record) => ( + {record.student_id===null?"--":record.student_id} + ) + }, + { + title: '状态', + key: 'attendance_status', + width:300, + dataIndex: 'attendance_status', + className: "textcenter", + render: (text, record) => ( + + + + + ), + }, + ]; + + return( + + + + + + + + + + + + + + + + + + + + + + + + + {this.props.defaultActiveKey==="2"? + 正常签到:{data&&data.normal_count} + 请假:{data&&data.leave_count} + 旷课:{data&&data.absence_count} + : + 已签到:{this.state.course_members_count} + 应签到:{this.state.attendance_count} + } + + + + + + +
+ { + this.state.loading===true? +
+ +
+ : + member_attendances&&member_attendances.length===0? +
+ {/**/} + +
+ + : + // loading={this.state.loading} + + + } + + + +
+ { + member_attendances&&member_attendances.length>0? + + :"" + } + +
+ + + ) + } + +} + + +export default Signedinlist diff --git a/public/react/src/modules/courses/signin/student/Signindetails.js b/public/react/src/modules/courses/signin/student/Signindetails.js new file mode 100644 index 000000000..a5df6c2b2 --- /dev/null +++ b/public/react/src/modules/courses/signin/student/Signindetails.js @@ -0,0 +1,77 @@ +import React,{ Component } from "react"; +import '../css/signincdi.css'; +import Detailss from '../component/Detailss'; +import Signedinlist from './Signedinlist' +import axios from 'axios'; + +//在线学习 +class Signindetails extends Component{ + constructor(props){ + super(props); + + this.state={ + headdata:null + } + + } + + componentDidMount() { + this.mygetdatas() + } + mygetdatas=()=>{ + const switattendance_id=this.props.switattendance_id; + let urls = `/weapps/attendances/${switattendance_id}.json`; + axios.get(urls).then((response) => { + //.log(response); + if(response){ + if(response.data){ + this.setState({ + headdata:response.data + }) + + } + } + }) + } + + + + render(){ + let {headdata}= this.state; + return( + +
+
+

+ this.props.qiandaoxiangq(false)}> +

this.props.qiandaoxiangq(false)}>正在签到

+

+ +
+ + + + {/* 列表+筛选 */} +
+ this.mygetdatas()} + defaultActiveKey={this.props.defaultActiveKey} + switattendance_id={this.props.switattendance_id} + headdata={headdata} + {...this.props} + {...this.state} + /> +
+
+
+ ) + } +} +export default Signindetails; diff --git a/public/react/src/modules/courses/signin/teacher/Teachers_signin.js b/public/react/src/modules/courses/signin/teacher/Teachers_signin.js new file mode 100644 index 000000000..bda49fa6a --- /dev/null +++ b/public/react/src/modules/courses/signin/teacher/Teachers_signin.js @@ -0,0 +1,394 @@ +import React,{ Component } from "react"; +import '../css/signincdi.css'; +import { Tabs,Pagination} from 'antd'; +import {queryString} from 'educoder'; +import Teaccomponent from '../component/teachercomponent/Teaccomponent'; +import Createsignmodel from '../model/Createsignmodel'; +import Studentssignmodels from "../model/Studentssignmodel"; +import Qrcodesignin from "../model/Qrcodesignin"; +import Signinname from '../model/Signinname'; +import Signinstatistics from "../Signinstatistics/Signinstatistics"; +import Signindetails from '../student/Signindetails'; + +import axios from 'axios'; + +const { TabPane } = Tabs; +//在线学习 + +class Students_signin extends Component{ + constructor(props){ + super(props); + + this.state={ + visible:false, + page:1, + limit:5, + count:50, + defaultActiveKey:"1", + attendances_count:0, + datas:[], + normal_count:0, + leave_count:0, + absence_count:0, + Studentssigntype:false, + Studentssigntypedata:null, + Qrcodesignintype:false, + Qrcodesignintypedata:null, + switchs:false, + switattendance_id:null, + Spin:false, + Signinnamestype:false, + mybianjiname:"", + + } + } + + componentDidMount() { + let data={ + key:"1", + page:1, + limit:5 + } + this.gogetdatas(data); + + // const query = this.props.location.search; + // const parsed = queryString.parse(query); + // console.log("parsed"); + // console.log(parsed); + } + + componentDidUpdate = (prevProps) => { + + + } + + callback=(key)=> { + //.log(key); + this.setState({ + defaultActiveKey: key, + page:1, + limit:5 + }) + let data={ + key:key, + page:1, + limit:5 + } + if(key==="1" || key ==="2"){ + this.gogetdatas(data); + } + } + + gogetdatas =(mydata)=>{ + this.setState({ + Spin:true + }) + const coureid=this.props.match.params.coursesId; + let urls = `/courses/${coureid}/attendances.json`; + let arry={} + if(mydata.key==="1" || mydata.key ===1){ + arry={ + page:mydata.page, + limit:mydata.limit, + }; + }else if(mydata.key==="2" || mydata.key ===2){ + arry={ + history:true, + page:mydata.page, + limit:mydata.limit, + } + } + axios.get(urls, { + params: arry + }).then((response) => { + //.log(response); + if(response){ + if(response.data){ + this.setState({ + attendances_count:response.data.attendances_count, + datas:response.data.attendances, + normal_count:response.data.normal_count, + leave_count:response.data.leave_count, + absence_count:response.data.absence_count, + }) + if(response.data.attendances.length===0&&mydata.page!==1){ + this.setState({ + page:mydata.page-1, + limit:5 + }) + let data={ + key: mydata.key, + page:mydata.page-1, + limit:5 + } + this.gogetdatas(data); + } + } + } + this.setState({ + Spin:false + }) + }).catch((error) => { + this.setState({ + Spin:false + }) + }) + } + + Createsign=()=>{ + this.setState({ + visible:true + }) + } + + + hideCreatesign=()=>{ + this.setState({ + visible:false + }) + } + + + paginationonChange = (pageNumber) => { + this.setState({ + page: pageNumber, + }) + + let data={ + key:this.state.defaultActiveKey, + page:pageNumber, + limit:5 + } + this.gogetdatas(data); + + } + + getsetdatas=()=>{ + let data={ + key:this.state.defaultActiveKey, + page:this.state.page, + limit:5 + } + this.gogetdatas(data); + } + + Gotomodes(data,bool){ + if(bool===true){ + this.setState({ + Studentssigntypedata:data, + Studentssigntype:true + }) + }else{ + this.setState({ + Studentssigntype:false, + Studentssigntypedata:null, + + }) + } + + + } + GotomQrcodesodesy(data,bool){ + if(bool===true){ + this.setState({ + Qrcodesignintype:true, + Qrcodesignintypedata:data, + }) + }else{ + this.setState({ + Qrcodesignintype:false, + Qrcodesignintypedata:null, + + }) + } + + + } + + qiandaoxiangq=(bool,id)=>{ + this.setState({ + switchs:bool, + switattendance_id:id, + }) + if(bool===false){ + this.getsetdatas(); + } + } + + + settabsdata=()=>{ + let data={ + defaultActiveKey:"1", + key:"1", + page:1, + limit:5 + } + this.gogetdatas(data); + } + + Signinnamestypes=(id,bool,name)=>{ + this.setState({ + switattendance_id:id, + Signinnamestype:bool, + mybianjiname:name, + }) + } + + + render(){ + + let {page,per_page,count,defaultActiveKey,limit,attendances_count,datas,absence_count,leave_count,normal_count, + Studentssigntype,Studentssigntypedata,Qrcodesignintype,Qrcodesignintypedata,switchs,switattendance_id,Spin, + mybianjiname + } =this.state; + const isAdmin =this.props.isAdmin(); + + return( + + +
+ {/*编辑名称*/} + this.Signinnamestypes(id,b,a)} + getsetdatas={()=>this.getsetdatas()} + /> + {/*创建实训*/} + {this.state.visible===true?this.hideCreatesign()} + settabsdata={()=>this.settabsdata()} + />:""} + {/*签到*/} + this.Gotomodes(d,b)} + getsetdatas={()=>this.getsetdatas()} + /> + {/*二维码*/} + this.getsetdatas()} + GotomQrcodesodesy={(d,b)=>this.GotomQrcodesodesy(d,b)} + /> + { + switchs===false? +
+
+ + +

共{attendances_count}个签到正在进行

+ this.Signinnamestypes(id,b,a)} + qiandaoxiangq={(b,id)=>this.qiandaoxiangq(b,id)} + Gotomodes={(d,b)=>this.Gotomodes(d,b)} + GotomQrcodesodesy={(d,b)=>this.GotomQrcodesodesy(d,b)} + getsetdatas={()=>this.getsetdatas()} + datas={datas} + defaultActiveKey={defaultActiveKey} + {...this.state} + {...this.props} + + > + + + +
+ {attendances_count>0?:''} +
+
+ +
+

已完成{attendances_count}次签到

+ { + isAdmin===false? +
+

旷课:{absence_count}

+

请假:{leave_count}

+

正常签到:{normal_count}

+ +
+ : + "" + } +
+ + + + this.Signinnamestypes(id,b,a)} + qiandaoxiangq={(b,id)=>this.qiandaoxiangq(b,id)} + Gotomodes={(d,b)=>this.Gotomodes(d,b)} + GotomQrcodesodesy={(d,b)=>this.GotomQrcodesodesy(d,b)} + getsetdatas={()=>this.getsetdatas()} + datas={datas} + defaultActiveKey={defaultActiveKey} + {...this.state} + {...this.props} + > + + +
+ {attendances_count>0?:'' + }
+
+ { + isAdmin===true? + + {defaultActiveKey&&defaultActiveKey==="3"?:""} + + : + "" + } + +
+ { + isAdmin? +
+ +

this.Createsign()}>创建签到

+
+ :"" + } +
+ +
+ : + // 签到详情 + this.qiandaoxiangq(b)} + > + + } + +
+
+ ) + } +} +export default Students_signin; diff --git a/public/react/src/modules/courses/videostatistics/Videostatistics.js b/public/react/src/modules/courses/videostatistics/Videostatistics.js new file mode 100644 index 000000000..52d69b0fb --- /dev/null +++ b/public/react/src/modules/courses/videostatistics/Videostatistics.js @@ -0,0 +1,94 @@ +import React,{ Component } from "react"; +import axios from 'axios'; +import '../signin/css/signincdi.css'; +import Videostatisticscom from './component/Videostatisticscom'; +import Videostatisticslist from './component/Videostatisticslist'; +import Videostatisticscomtwo from './component/Videostatisticscomtwo'; + + +//在线学习 +class Videostatistics extends Component{ + constructor(props){ + super(props); + this.state={ + watch_staticsdata:[], + tisticsbool:false, + tisid:null, + } + + + } + + componentDidMount() { + this.togetdatas(); + } + details=()=>{ + + } + + togetdatas(){ + const CourseId=this.props.match.params.coursesId; + let url=`/courses/${CourseId}/watch_statics.json`; + axios.get(url).then((response) => { + if(response){ + this.setState({ + watch_staticsdata:response.data, + }) + + } + + }).catch((error) => { + + + }); + } + + tisticsbools=(bool,id)=>{ + this.setState({ + tisticsbool:bool, + tisid:id, + }) + } + + + render(){ + let {watch_staticsdata,tisticsbool,tisid}= this.state; + + return( + +
+ { + tisticsbool===false? +
+
+

this.props.statisticsy(false)}> + +

返回

+

+ +
+ + + + +
+ this.tisticsbools(b,id)}> + + +
+
+ : + this.tisticsbools(b,id)}> + } + +
+
+ ) + } +} +export default Videostatistics; diff --git a/public/react/src/modules/courses/videostatistics/component/Videostatisticscom.js b/public/react/src/modules/courses/videostatistics/component/Videostatisticscom.js new file mode 100644 index 000000000..457c6d1f3 --- /dev/null +++ b/public/react/src/modules/courses/videostatistics/component/Videostatisticscom.js @@ -0,0 +1,103 @@ +import React, {Component} from "react"; +import '../../signin/css/signincdi.css'; +import {Progress, message} from 'antd'; +import {getImageUrl} from 'educoder'; +import axios from 'axios'; + + + +//条目 +class Videostatisticscom extends Component { + //条目组件 + constructor(props) { + super(props); + + this.state = {} + } + + componentDidMount() { + + } + + componentDidUpdate = (prevProps) => { + + + } + + + + render() { + + return ( + +
+
+
+
视频统计总览
+
播放数据从{this.props.watch_staticsdata&&this.props.watch_staticsdata.begin_at?this.props.watch_staticsdata.begin_at:0}开始统计
+
+ +
+
+
+
观看总人数(人)
+
{this.props.watch_staticsdata&&this.props.watch_staticsdata.people_num?this.props.watch_staticsdata.people_num:0}
+ +
+ +
+ +
+
+
观看总人次(次)
+
{this.props.watch_staticsdata&&this.props.watch_staticsdata.freq?this.props.watch_staticsdata.freq:0}
+
+
+ +
+
+
总观看时长(时)
+
{this.props.watch_staticsdata&&this.props.watch_staticsdata.total_duration?this.props.watch_staticsdata.total_duration:0}
+
+
+ +
+ + + +
+ +
+ +
+ ) + } +} + +export default Videostatisticscom; diff --git a/public/react/src/modules/courses/videostatistics/component/Videostatisticscomtwo.js b/public/react/src/modules/courses/videostatistics/component/Videostatisticscomtwo.js new file mode 100644 index 000000000..764ddcb1f --- /dev/null +++ b/public/react/src/modules/courses/videostatistics/component/Videostatisticscomtwo.js @@ -0,0 +1,504 @@ +import React, {Component} from "react"; +import '../../signin/css/signincdi.css'; +import {Pagination, Table, Menu, Dropdown} from 'antd'; +import {getImageUrl, sortDirections} from 'educoder'; +import axios from 'axios'; +import LoadingSpin from "../../../../common/LoadingSpin"; +import NoneDatas from "../../signin/component/NoneDatas"; + + +//条目 +class Videostatisticscomtwo extends Component { + //条目组件 + constructor(props) { + super(props); + + this.state = { + data: [], + page: 1, + limit: 10, + members_count: 0, + columnsstu: [ + { + title: '序号', + dataIndex: 'number', + key: 'number', + align: "center", + className: 'font-14', + width: '50px', + render: (text, record) => ( + {record.number} + ), + }, + { + title: '用户', + dataIndex: 'user_name', + key: 'user_name', + align: "center", + className: 'font-14 maxnamewidth100s', + width: '100px', + render: (text, record) => ( + {record.user_name} + ), + }, + { + title: '视频是否看完', + dataIndex: 'is_finished', + key: 'is_finished', + align: "center", + className: 'font-14', + width: '98px', + render: (text, record) => ( + {record.is_finished === true ? + : } + ), + }, + { + title: '视频累计观看时长', + dataIndex: 'total_duration', + key: 'total_duration', + align: "center", + className: 'font-14 maxnamewidth150s', + width: '150px', + sorter: true, + sortDirections: sortDirections, + render: (text, record) => ( + {record.total_duration} + ), + }, + { + title: '累计观看次数(次)', + dataIndex: 'feq', + key: 'feq', + align: "center", + className: 'font-14 maxnamewidth100s', + width: '100px', + sorter: true, + sortDirections: sortDirections, + render: (text, record) => ( + {record.feq} + ), + }, + { + title: '最早观看时间', + dataIndex: 'start_at', + key: 'start_at', + align: "center", + className: 'font-14 maxnamewidth100s', + width: '100px', + render: (text, record) => ( + {record.start_at} + ), + }, + { + title: '最后观看时间', + dataIndex: 'end_at', + key: 'end_at', + align: "center", + className: 'font-14 maxnamewidth100s', + width: '100px', + render: (text, record) => ( + {record.end_at} + ), + } + ], + loading: false, + order: undefined, + course_groups: [], + fbbool: false, + groupsid: null, + } + } + + componentDidMount() { + this.setState({ + order: undefined + }) + if (this.props.isAdmin()) { + //老师 + const CourseId = this.props.match.params.coursesId; + + var data = { + id: CourseId, + page: this.state.page, + } + this.getdatas(data); + + } else { + //学生 + var data = { + page: this.state.page, + } + this.getdatas(data); + } + this.fenbans(); + } + + componentDidUpdate = (prevProps) => { + + + } + + //分班 + fenbans = () => { + const CourseId = this.props.match.params.coursesId; + let url = `/courses/${CourseId}/course_groups.json`; + axios.get(url).then((response) => { + if (response) { + console.log("分班"); + console.log("response"); + console.log(response); + this.setState({ + course_groups: response.data.course_groups, + current_group_id: response.data.current_group_id, + none_group_member_count: response.data.none_group_member_count, + group_count: response.data.group_count, + }) + } + + }).catch((error) => { + + }); + } + + //学生 + getdatas = (data) => { + this.setState({ + loading: true + }) + const CourseId = this.props.match.params.coursesId; + let url = ""; + if (this.props.isAdmin()) { + url = `/course_videos/${this.props.tisid}/watch_histories.json`; + } else { + url = `/courses/${CourseId}/own_watch_histories.json`; + } + axios.get(url, {params: data}).then((response) => { + if (response) { + if (response.data) { + if (response.data.data.length > 0) { + let datalists = []; + for (var i = 0; i < response.data.data.length; i++) { + datalists.push({ + number: (parseInt(this.state.page) - 1) * parseInt(this.state.limit) + (i + 1), + user_name: response.data.data[i].user_name, + is_finished: response.data.data[i].is_finished, + total_duration: response.data.data[i].total_duration, + feq: response.data.data[i].feq, + start_at: response.data.data[i].start_at, + end_at: response.data.data[i].end_at, + }) + } + + this.setState({ + data: datalists, + members_count: response.data.count, + }) + } else { + this.setState({ + data: [], + members_count: response.data.count, + }) + } + } else { + this.setState({ + data: [], + members_count: response.data.count, + }) + } + + + } + this.setState({ + loading: false + }) + }).catch((error) => { + this.setState({ + loading: false + }) + }); + } + + + paginationonChange = (pageNumber) => { + this.setState({ + page: pageNumber, + }) + let data = {} + if (this.props.isAdmin()) { + //老师 + const CourseId = this.props.match.params.coursesId; + data = { + id: CourseId, + page: pageNumber, + group_id: this.state.groupsid, + order: this.state.order, + } + } else { + //学生 + data = { + page: pageNumber, + order: this.state.order, + } + } + this.getdatas(data); + } + fenbanone = () => { + if (this.state.fbbool === false) { + this.setState({ + fbbool: true + }) + } else { + this.setState({ + fbbool: false + }) + } + } + setcourse_groups = (id) => { + this.setState({ + groupsid: id + }) +//老师 + const CourseId = this.props.match.params.coursesId; + + var data = {}; + if (id) { + data = { + id: CourseId, + page: this.state.page, + group_id: id + } + + } else { + data = { + id: CourseId, + page: this.state.page + } + } + this.getdatas(data); + } + + //实训作业tbale 列表塞选数据 + table1handleChange = (pagination, filters, sorter) => { + if (JSON.stringify(sorter) === "{}") { + //没有选择 + } else { + try { + //学生学号排序 + if (sorter.columnKey === "total_duration" || sorter.columnKey === "feq") { + let mysorder = ""; + if (sorter.order === "ascend") { + if (sorter.columnKey === "total_duration") { + mysorder = "total_duration-asc"; + } else { + mysorder = "freq-asc"; + + } + //升序 + let data = {} + if (this.props.isAdmin()) { + //老师 + const CourseId = this.props.match.params.coursesId; + if (this.state.groupsid) { + data = { + id: CourseId, + page: this.state.page, + group_id: this.state.groupsid, + order: mysorder, + } + } else { + data = { + id: CourseId, + page: this.state.page, + order: mysorder, + } + } + } else { + //学生 + data = { + page: this.state.page, + order: mysorder, + } + } + + this.getdatas(data); + this.setState({ + order: mysorder, + }) + } else if (sorter.order === "descend") { + if (sorter.columnKey === "total_duration") { + mysorder = "total_duration-desc"; + + } else { + mysorder = "freq-desc"; + + } + //降序 + let data = {} + if (this.props.isAdmin()) { + //老师 + const CourseId = this.props.match.params.coursesId; + if (this.state.groupsid) { + data = { + id: CourseId, + page: this.state.page, + group_id: this.state.groupsid, + order: mysorder, + } + } else { + data = { + id: CourseId, + page: this.state.page, + order: mysorder, + } + } + } else { + //学生 + data = { + page: this.state.page, + order: mysorder, + } + } + + this.getdatas(data); + this.setState({ + order: mysorder, + }) + + + } + } + } catch (e) { + + } + + } + + } + + + render() { + let {loading, data, page, limit, members_count, columnsstu, fbbool, course_groups} = this.state; + const isAdmin = this.props.isAdmin(); + + const menu = ( + + + this.setcourse_groups(null)}> +

全部

+
+
+ { + course_groups && course_groups.length > 0 ? + ( + course_groups.map((item, key) => { + return ( + + this.setcourse_groups(item.id)} key={key}> +

{item.name}

+
+
+ ) + }) + ) + : + "" + } +
+ ); + return ( + +
+
+
+
视频名称视频名称…
+
+
this.props.tisticsbools(false, null)}> + 视频统计总览 +
+ { + isAdmin === true ? +
this.fenbanone()}> + trigger.parentNode} overlay={menu} + placement="bottomCenter"> + + 分班 + { + fbbool === true ? + + : + + } + + +
+ : + "" + } + +
+
+ + + + { + loading === true ? +
+ +
+ : +
+ { + data.length === 0 ? +
+ +
+ : +
+ } + + + + } + + + + +
+ { + data && data.length > 0 ? + + : "" + } + +
+ + + + ) + } +} + +export default Videostatisticscomtwo; diff --git a/public/react/src/modules/courses/videostatistics/component/Videostatisticslist.js b/public/react/src/modules/courses/videostatistics/component/Videostatisticslist.js new file mode 100644 index 000000000..637ec306c --- /dev/null +++ b/public/react/src/modules/courses/videostatistics/component/Videostatisticslist.js @@ -0,0 +1,302 @@ +import React, {Component} from "react"; +import '../../signin/css/signincdi.css'; +import {Pagination,Table} from 'antd'; +import {getImageUrl,sortDirections} from 'educoder'; +import axios from 'axios'; +import LoadingSpin from "../../../../common/LoadingSpin"; +import NoneDatas from "../../signin/component/NoneDatas"; + + +//条目 +class Videostatisticslist extends Component { + //条目组件 + constructor(props) { + super(props); + + this.state = { + columnsstu: [ + { + title: '序号', + dataIndex: 'number', + key: 'number', + align: "center", + className: 'font-14', + width: '90px', + render: (text, record) => ( + {record.number} + ), + }, + { + title: '视频名称', + dataIndex: 'title', + key: 'title', + align: "center", + className: 'font-14 maxnamewidth150s', + width: '150px', + render: (text, record) => ( + {record.title} + ), + }, + { + title: '观看人数(人)', + dataIndex: 'people_num', + key: 'people_num', + align: "center", + className: 'font-14', + width: '98px', + sorter: true, + sortDirections: sortDirections, + render: (text, record) => ( + {record.people_num} + ), + }, + { + title: '累计观看时长', + dataIndex: 'total_time', + key: 'total_time', + align: "center", + className: 'font-14 maxnamewidth150s', + width: '150px', + sorter: true, + sortDirections: sortDirections, + render: (text, record) => ( + {record.total_time} + ), + }, + { + title: '发布人', + dataIndex: 'user_name', + key: 'user_name', + align: "center", + className: 'font-14 maxnamewidth100s', + width: '100px', + render: (text, record) => ( + {record.user_name} + ), + }, + { + title: '详情', + dataIndex: 'id', + key: 'id', + align: "center", + className: 'font-14', + width: '90px', + render: (text, record) => ( + this.props.tisticsbools(true,record.id)}>详情 + ), + } + ], + loading:false, + data:[], + page:1, + limit:10, + members_count:0, + order:undefined, + } + } + + componentDidMount() { + let data={ + page:1, + order:this.state.order + } + this.togetdatas(data); + + } + + componentDidUpdate = (prevProps) => { + + + } + paginationonChange = (pageNumber) => { + this.setState({ + page: pageNumber, + }) + let data={ + page:pageNumber, + order:this.state.order + } + this.togetdatas(data); + } + + + togetdatas(data){ + this.setState({ + loading:true + }) + const CourseId=this.props.match.params.coursesId; + let url=`/courses/${CourseId}/watch_video_histories.json`; + axios.get(url,{params:data + }).then((response) => { + if (response) { + if (response.data) { + if (response.data.videos.length > 0) { + let datalists = []; + for (var i = 0; i < response.data.videos.length; i++) { + datalists.push({ + number: (parseInt(this.state.spage) - 1) * parseInt(this.state.limit) + (i + 1), + title: response.data.videos[i].title, + people_num: response.data.videos[i].people_num, + total_time: response.data.videos[i].total_time, + user_name: response.data.videos[i].user_name, + id: response.data.videos[i].id, + }) + } + + this.setState({ + data: datalists, + members_count: response.data.count, + }) + } else { + this.setState({ + data: [], + members_count: response.data.count, + }) + } + } else { + this.setState({ + data: [], + members_count: response.data.count, + }) + } + + + } + + + + this.setState({ + loading:false + }) + }).catch((error) => { + this.setState({ + loading:false + }) + }); + } + + + //实训作业tbale 列表塞选数据 + table1handleChange = (pagination, filters, sorter) => { + if (JSON.stringify(sorter) === "{}") { + //没有选择 + } else { + try { + //学生学号排序 + if (sorter.columnKey === "people_num"||sorter.columnKey === "total_time") { + let mysorder=""; + if (sorter.order === "ascend") { + if(sorter.columnKey === "people_num"){ + mysorder="people_num-asc"; + }else{ + mysorder="total_time-asc"; + + } + //升序 + let data={ + page:this.state.page, + order:mysorder + } + this.togetdatas(data); + this.setState({ + order: mysorder, + }) + } else if (sorter.order === "descend") { + if(sorter.columnKey === "people_num"){ + mysorder="people_num-desc"; + }else{ + mysorder="total_time-desc"; + + } + //降序 + let data={ + page:this.state.page, + order:mysorder + } + this.togetdatas(data); + this.setState({ + order: mysorder, + }) + } + } + } catch (e) { + + } + + } + + } + + + + render() { + let {loading,data,columnsstu,page,members_count,limit}=this.state; + return ( + +
+
+
+
+
统计详情
+
+ +
+ + { + loading===true? +
+ +
+ : +
+ { + data.length===0? +
+ +
+ : +
+ } + + + + } + + + +
+ { + data&&data.length>0? + + :"" + } + +
+ + + + ) + } +} + +export default Videostatisticslist; diff --git a/public/react/src/modules/login/Trialapplication.js b/public/react/src/modules/login/Trialapplication.js index be0b18edb..5cb181663 100644 --- a/public/react/src/modules/login/Trialapplication.js +++ b/public/react/src/modules/login/Trialapplication.js @@ -386,7 +386,7 @@ class Trialapplication extends Component { { isRenders === false ? "" : -
+
- 头像 -

修改头像

+ 头像 +
修改头像
); } diff --git a/public/react/src/modules/user/usersInfo/video/InfosVideo.css b/public/react/src/modules/user/usersInfo/video/InfosVideo.css index fb78f9e85..a03a2c6f6 100644 --- a/public/react/src/modules/user/usersInfo/video/InfosVideo.css +++ b/public/react/src/modules/user/usersInfo/video/InfosVideo.css @@ -70,6 +70,7 @@ } .videoItem .time { + height: 15px; color: #C0C4CC; } .videoItem .square-main .buttonRow .dianjilianicon{ diff --git a/public/react/src/modules/user/usersInfo/video/VideoInReviewItem.js b/public/react/src/modules/user/usersInfo/video/VideoInReviewItem.js index 4e0ca8fb2..299af0d08 100644 --- a/public/react/src/modules/user/usersInfo/video/VideoInReviewItem.js +++ b/public/react/src/modules/user/usersInfo/video/VideoInReviewItem.js @@ -1,6 +1,6 @@ import React, { useState, useEffect, useContext, memo } from 'react'; import { Progress, Input, Tooltip , Spin } from 'antd' -import { getUrl2, isDev, CBreadcrumb, ActionBtn, ThemeContext } from 'educoder' +import { getUrl2, isDev, CBreadcrumb, ActionBtn, ThemeContext,formatSeconds} from 'educoder' import axios from 'axios' import moment from 'moment' import playIcon from './images/play.png' @@ -21,7 +21,7 @@ const clipboardMap = {} function VideoInReviewItem (props) { const theme = useContext(ThemeContext); const { history, file_url , play_url , cover_url , transcoded , title, created_at, published_at, isReview, id - , onEditVideo, onMaskClick, getCopyText, showNotification,vv,play_duration,operation , deleteVideo , moveVideo ,link} = props; + , onEditVideo, onMaskClick, getCopyText, showNotification,vv,play_duration,operation , deleteVideo , moveVideo ,link, people_num,total_time} = props; useEffect(()=> { if (!isReview) { _clipboard = new ClipboardJS(`.copybtn_item_${id}`); @@ -37,6 +37,9 @@ function VideoInReviewItem (props) { } } }, []) + + + const username = props.match.params.username function toList() { history.push(`/users/${username}/videos`) @@ -60,7 +63,7 @@ function VideoInReviewItem (props) { {!isReview && !link && transcoded &&
onMaskClick(props)}> - {play_duration===0?"":
累计学习时长:{play_duration} h
} + {/* {play_duration===0?"":
累计学习时长:{play_duration} h
} */}
} @@ -69,16 +72,22 @@ function VideoInReviewItem (props) { title={title && title.length > 20 ? title : ''} >{title}
- - {moment(published_at || created_at).format('YYYY-MM-DD HH:mm:ss')} + {/*
累计学习时长:{play_duration} h
*/} + {/* {moment(published_at || created_at).format('YYYY-MM-DD HH:mm:ss')}{people_num} */} + {link ? :累计学习时长:{ + formatSeconds(total_time)} + {/* total_time<60?total_time+' s':total_time/60<60?(total_time/60).toFixed(0)+' min':(total_time/3600).toFixed(1)+ ' h' */} + }
{/* 2019-09-01 10:00:22 */} - {!vv || (vv && vv)===0 ? "" : - - } {!vv || (vv && vv)===0?"":vv} + {!people_num || (people_num && people_num)===0 ? "" : + {!people_num || (people_num && people_num)===0?"":people_num} + } + +
{ isReview !== true && moveVideo && diff --git a/public/stylesheets/educoder/iconfont/demo_index.html b/public/stylesheets/educoder/iconfont/demo_index.html index 281ac40d0..7a6d6b5e8 100644 --- a/public/stylesheets/educoder/iconfont/demo_index.html +++ b/public/stylesheets/educoder/iconfont/demo_index.html @@ -30,6 +30,84 @@
    +
  • + +
    返回
    +
    &#xe739;
    +
  • + +
  • + +
    统计
    +
    &#xe738;
    +
  • + +
  • + +
    签到-02
    +
    &#xe736;
    +
  • + +
  • + +
    下箭头
    +
    &#xe737;
    +
  • + +
  • + +
    签到-01
    +
    &#xe735;
    +
  • + +
  • + +
    移动
    +
    &#xe734;
    +
  • + +
  • + +
    下移2
    +
    &#xe732;
    +
  • + +
  • + +
    上移2
    +
    &#xe731;
    +
  • + +
  • + +
    下移
    +
    &#xe730;
    +
  • + +
  • + +
    上移
    +
    &#xe72f;
    +
  • + +
  • + +
    编辑
    +
    &#xe72e;
    +
  • + +
  • + +
    删除
    +
    &#xe72d;
    +
  • + +
  • + +
    选择
    +
    &#xe72c;
    +
  • +
  • 编辑
    @@ -2012,6 +2090,123 @@
      +
    • + +
      + 返回 +
      +
      .icon-fanhui +
      +
    • + +
    • + +
      + 统计 +
      +
      .icon-tongji1 +
      +
    • + +
    • + +
      + 签到-02 +
      +
      .icon-qiandao-1 +
      +
    • + +
    • + +
      + 下箭头 +
      +
      .icon-xiajiantou2 +
      +
    • + +
    • + +
      + 签到-01 +
      +
      .icon-qiandao- +
      +
    • + +
    • + +
      + 移动 +
      +
      .icon-yidong +
      +
    • + +
    • + +
      + 下移2 +
      +
      .icon-xiayi1 +
      +
    • + +
    • + +
      + 上移2 +
      +
      .icon-shangyi1 +
      +
    • + +
    • + +
      + 下移 +
      +
      .icon-xiayi +
      +
    • + +
    • + +
      + 上移 +
      +
      .icon-shangyi +
      +
    • + +
    • + +
      + 编辑 +
      +
      .icon-bianji5 +
      +
    • + +
    • + +
      + 删除 +
      +
      .icon-shanchu3 +
      +
    • + +
    • + +
      + 选择 +
      +
      .icon-xuanze +
      +
    • +
    • @@ -4939,6 +5134,110 @@
        +
      • + +
        返回
        +
        #icon-fanhui
        +
      • + +
      • + +
        统计
        +
        #icon-tongji1
        +
      • + +
      • + +
        签到-02
        +
        #icon-qiandao-1
        +
      • + +
      • + +
        下箭头
        +
        #icon-xiajiantou2
        +
      • + +
      • + +
        签到-01
        +
        #icon-qiandao-
        +
      • + +
      • + +
        移动
        +
        #icon-yidong
        +
      • + +
      • + +
        下移2
        +
        #icon-xiayi1
        +
      • + +
      • + +
        上移2
        +
        #icon-shangyi1
        +
      • + +
      • + +
        下移
        +
        #icon-xiayi
        +
      • + +
      • + +
        上移
        +
        #icon-shangyi
        +
      • + +
      • + +
        编辑
        +
        #icon-bianji5
        +
      • + +
      • + +
        删除
        +
        #icon-shanchu3
        +
      • + +
      • + +
        选择
        +
        #icon-xuanze
        +
      • +