Merge branches 'courseware' and 'dev_aliyun' of https://bdgit.educoder.net/Hjqreturn/educoder into courseware

courseware
杨树明 5 years ago
commit b56a66c8fb

3
.gitignore vendored

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

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

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

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

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

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

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

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

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

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

@ -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}")

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

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

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

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

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

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

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

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

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

@ -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个字符" }

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

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

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

@ -5,9 +5,9 @@
</td>
<td><%= course.course_members_count %></td>
<td><%= get_attachment_count(course, 0) %></td>
<td><%= course.course_homework_count(1) %></td>
<td><%= course.course_homework_count(3) %></td>
<td><%= course.course_homework_count(4) %></td>
<td><%= course.course_homework_count("normal") %></td>
<td><%= course.course_homework_count("group") %></td>
<td><%= course.course_homework_count("practice") %></td>
<td><%= course.exercises_count %></td>
<td><%= course.evaluate_count %></td>
<td><%= course.is_public == 1 ? "--" : "√" %></td>

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

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

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

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

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

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

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

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

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

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

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

@ -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
json.end_time @homework.end_time
json.late_time @homework.late_time

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

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

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

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

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

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

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

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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

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

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

@ -30,6 +30,36 @@
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xe739;</span>
<div class="name">返回</div>
<div class="code-name">&amp;#xe739;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe738;</span>
<div class="name">统计</div>
<div class="code-name">&amp;#xe738;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe736;</span>
<div class="name">签到-02</div>
<div class="code-name">&amp;#xe736;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe737;</span>
<div class="name">下箭头</div>
<div class="code-name">&amp;#xe737;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe735;</span>
<div class="name">签到-01</div>
<div class="code-name">&amp;#xe735;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe734;</span>
<div class="name">移动</div>
@ -2060,6 +2090,51 @@
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont icon-fanhui"></span>
<div class="name">
返回
</div>
<div class="code-name">.icon-fanhui
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-tongji1"></span>
<div class="name">
统计
</div>
<div class="code-name">.icon-tongji1
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-qiandao-1"></span>
<div class="name">
签到-02
</div>
<div class="code-name">.icon-qiandao-1
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-xiajiantou2"></span>
<div class="name">
下箭头
</div>
<div class="code-name">.icon-xiajiantou2
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-qiandao-"></span>
<div class="name">
签到-01
</div>
<div class="code-name">.icon-qiandao-
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-yidong"></span>
<div class="name">
@ -5059,6 +5134,46 @@
<div class="content symbol">
<ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-fanhui"></use>
</svg>
<div class="name">返回</div>
<div class="code-name">#icon-fanhui</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-tongji1"></use>
</svg>
<div class="name">统计</div>
<div class="code-name">#icon-tongji1</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-qiandao-1"></use>
</svg>
<div class="name">签到-02</div>
<div class="code-name">#icon-qiandao-1</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-xiajiantou2"></use>
</svg>
<div class="name">下箭头</div>
<div class="code-name">#icon-xiajiantou2</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-qiandao-"></use>
</svg>
<div class="name">签到-01</div>
<div class="code-name">#icon-qiandao-</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-yidong"></use>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -5,6 +5,41 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "13458315",
"name": "返回",
"font_class": "fanhui",
"unicode": "e739",
"unicode_decimal": 59193
},
{
"icon_id": "13458287",
"name": "统计",
"font_class": "tongji1",
"unicode": "e738",
"unicode_decimal": 59192
},
{
"icon_id": "13428689",
"name": "签到-02",
"font_class": "qiandao-1",
"unicode": "e736",
"unicode_decimal": 59190
},
{
"icon_id": "13428658",
"name": "下箭头",
"font_class": "xiajiantou2",
"unicode": "e737",
"unicode_decimal": 59191
},
{
"icon_id": "13427273",
"name": "签到-01",
"font_class": "qiandao-",
"unicode": "e735",
"unicode_decimal": 59189
},
{
"icon_id": "13353315",
"name": "移动",

@ -20,6 +20,21 @@ Created by iconfont
/>
<missing-glyph />
<glyph glyph-name="fanhui" unicode="&#59193;" d="M590.769231-107.362462H315.076923c-31.507692 0-63.015385 23.630769-63.015385 63.015385s23.630769 63.015385 63.015385 63.015385h275.692308c259.938462 0 378.092308 7.876923 378.092307 252.061538s-118.153846 252.061538-378.092307 252.061539H315.076923c-31.507692 0-63.015385 23.630769-63.015385 63.015384s31.507692 55.138462 63.015385 55.138462h275.692308c228.430769 0 496.246154 0 496.246154-378.092308s-267.815385-370.215385-496.246154-370.215385zM47.261538 514.914462l259.938462-196.923077c39.384615-23.630769 86.646154-15.753846 110.276923 15.753846 7.876923 15.753846 15.753846 31.507692 15.753846 47.261538v393.846154c0 47.261538-31.507692 78.769231-78.769231 78.769231-15.753846 0-31.507692-7.876923-47.261538-15.753846l-259.938462-196.923077c-39.384615-31.507692-47.261538-78.769231-15.753846-110.276923 0-7.876923 7.876923-15.753846 15.753846-15.753846z" horiz-adv-x="1102" />
<glyph glyph-name="tongji1" unicode="&#59192;" d="M702.171429 52.419048c-78.019048 0-141.409524 63.390476-141.409524 141.409523V754.590476c0 78.019048 63.390476 141.409524 141.409524 141.409524 78.019048 0 141.409524-63.390476 141.409523-141.409524v-565.638095c-4.87619-73.142857-68.266667-136.533333-141.409523-136.533333z m0 746.057142c-24.380952 0-43.885714-19.504762-43.885715-43.885714v-565.638095c0-24.380952 19.504762-43.885714 43.885715-43.885714s43.885714 19.504762 43.885714 43.885714V754.590476c-4.87619 24.380952-24.380952 43.885714-43.885714 43.885714zM1067.885714 52.419048c-78.019048 0-141.409524 63.390476-141.409524 141.409523V447.390476c0 78.019048 63.390476 141.409524 141.409524 141.409524s141.409524-63.390476 141.409524-141.409524v-253.561905c0-78.019048-63.390476-141.409524-141.409524-141.409523z m0 433.980952c-24.380952 0-43.885714-19.504762-43.885714-43.885714v-253.561905c0-24.380952 19.504762-43.885714 43.885714-43.885714s43.885714 19.504762 43.885715 43.885714V447.390476c0 19.504762-19.504762 39.009524-43.885715 39.009524zM331.580952 52.419048C253.561905 52.419048 195.047619 115.809524 195.047619 193.828571V447.390476c0 78.019048 63.390476 141.409524 141.409524 141.409524 78.019048 0 141.409524-63.390476 141.409524-141.409524v-253.561905c-4.87619-78.019048-68.266667-141.409524-146.285715-141.409523z m0 433.980952c-19.504762 0-39.009524-19.504762-39.009523-39.009524v-253.561905c0-24.380952 19.504762-43.885714 43.885714-43.885714s43.885714 19.504762 43.885714 43.885714V447.390476c-4.87619 19.504762-24.380952 39.009524-48.761905 39.009524zM1297.066667-108.495238H48.761905c-29.257143 0-48.761905 19.504762-48.761905 48.761905v536.380952c0 29.257143 19.504762 48.761905 48.761905 48.761905s48.761905-19.504762 48.761905-48.761905v-487.619048h1199.542857c29.257143 0 48.761905-19.504762 48.761904-48.761904s-24.380952-48.761905-48.761904-48.761905z" horiz-adv-x="1365" />
<glyph glyph-name="qiandao-1" unicode="&#59190;" d="M881.509434-128h-724.528302a157.584906 157.584906 0 0 0-156.981132 156.981132v603.773585a157.584906 157.584906 0 0 0 156.981132 156.981132h724.528302a157.584906 157.584906 0 0 0 156.981132-156.981132v-603.773585a157.584906 157.584906 0 0 0-156.981132-156.981132z m-724.528302 845.283019a84.528302 84.528302 0 0 1-84.528302-84.528302v-603.773585a84.528302 84.528302 0 0 1 84.528302-84.528302h724.528302a84.528302 84.528302 0 0 1 84.528302 84.528302v603.773585a84.528302 84.528302 0 0 1-84.528302 84.528302zM277.735849 859.773585v-241.509434M277.735849 582.037736a36.226415 36.226415 0 0 0-36.226415 36.226415v241.509434a36.226415 36.226415 0 1 0 72.45283 0v-241.509434a36.226415 36.226415 0 0 0-36.226415-36.226415zM760.754717 859.773585v-241.509434M760.754717 582.037736a36.226415 36.226415 0 0 0-36.226415 36.226415v241.509434a36.226415 36.226415 0 0 0 72.45283 0v-241.509434a36.226415 36.226415 0 0 0-36.226415-36.226415zM486.641509 149.735849a37.433962 37.433962 0 0 0-27.773584 10.867925l-175.09434 181.132075a36.226415 36.226415 0 0 0 0 51.320755 36.830189 36.830189 0 0 0 51.320755 0l152.754717-158.792453 281.962264 221.584906a36.226415 36.226415 0 0 0 50.716981-6.037736 36.830189 36.830189 0 0 0-6.037736-51.320755l-307.320755-241.509434a38.037736 38.037736 0 0 0-20.528302-7.245283z" horiz-adv-x="1038" />
<glyph glyph-name="xiajiantou2" unicode="&#59191;" d="M796.390468-127.928326a113.768929 113.768929 0 0 0-86.464386 39.819126l-682.613575 796.382503A113.768929 113.768929 0 1 0 200.24128 856.172911L796.390468 156.493997 1392.539656 851.622154a113.768929 113.768929 0 0 0 172.928773-147.899608l-682.613575-796.382504A113.768929 113.768929 0 0 0 796.390468-127.928326z" horiz-adv-x="1592" />
<glyph glyph-name="qiandao-" unicode="&#59189;" d="M881.509434-128h-724.528302a157.584906 157.584906 0 0 0-156.981132 156.981132v603.773585a157.584906 157.584906 0 0 0 156.981132 156.981132h724.528302a157.584906 157.584906 0 0 0 156.981132-156.981132v-603.773585a157.584906 157.584906 0 0 0-156.981132-156.981132z m-724.528302 845.283019a84.528302 84.528302 0 0 1-84.528302-84.528302v-603.773585a84.528302 84.528302 0 0 1 84.528302-84.528302h724.528302a84.528302 84.528302 0 0 1 84.528302 84.528302v603.773585a84.528302 84.528302 0 0 1-84.528302 84.528302zM277.735849 859.773585v-241.509434M277.735849 582.037736a36.226415 36.226415 0 0 0-36.226415 36.226415v241.509434a36.226415 36.226415 0 1 0 72.45283 0v-241.509434a36.226415 36.226415 0 0 0-36.226415-36.226415zM760.754717 859.773585v-241.509434M760.754717 582.037736a36.226415 36.226415 0 0 0-36.226415 36.226415v241.509434a36.226415 36.226415 0 0 0 72.45283 0v-241.509434a36.226415 36.226415 0 0 0-36.226415-36.226415zM486.641509 149.73584900000003a37.433962 37.433962 0 0 0-27.773584 10.867925l-175.09434 181.132075a36.226415 36.226415 0 0 0 0 51.320755 36.830189 36.830189 0 0 0 51.320755 0l152.754717-158.792453 281.962264 221.584906a36.226415 36.226415 0 0 0 50.716981-6.037736 36.830189 36.830189 0 0 0-6.037736-51.320755l-307.320755-241.509434a38.037736 38.037736 0 0 0-20.528302-7.245283z" horiz-adv-x="1038" />
<glyph glyph-name="yidong" unicode="&#59188;" d="M855.341176-121.976471H174.682353c-90.352941 0-156.611765 72.282353-156.611765 156.611765V721.317647C18.070588 811.670588 90.352941 877.929412 174.682353 877.929412h680.658823c90.352941 0 156.611765-72.282353 156.611765-156.611765v-78.305882h-90.352941V721.317647c0 36.141176-30.117647 66.258824-66.258824 66.258824H174.682353c-36.141176 0-66.258824-30.117647-66.258824-66.258824v-680.658823c0-36.141176 30.117647-66.258824 66.258824-66.258824h680.658823c36.141176 0 66.258824 30.117647 66.258824 66.258824v78.305882h90.352941v-78.305882c6.023529-90.352941-66.258824-162.635294-156.611765-162.635295zM951.717647 299.670588H271.058824c-24.094118 0-48.188235 18.070588-48.188236 48.188236s18.070588 48.188235 48.188236 48.188235h680.658823c24.094118 0 48.188235-18.070588 48.188235-48.188235s-24.094118-48.188235-48.188235-48.188236zM1084.235294 347.858824L921.6 173.176471V534.588235L1084.235294 359.905882v-12.047058z" horiz-adv-x="1084" />

Before

Width:  |  Height:  |  Size: 404 KiB

After

Width:  |  Height:  |  Size: 410 KiB

@ -42,11 +42,11 @@ if (isDev) {
window.location.search.indexOf('debug=a') != -1 ? 'admin' : parsed.debug || 'admin'
}
// 超管
//debugType="admin";
debugType="admin";
// 老师
// debugType="teacher";
// 学生
// debugType="student";
//debugType="student";

@ -0,0 +1,9 @@
import React from 'react'
import MiniPagination from './components/mini-pagination'
export default () => {
function onPageChange(page) {
console.log(page, '-----------')
}
return <MiniPagination onChange={onPageChange} current={1} total={100} pageSize={16} />
}

@ -948,7 +948,7 @@ class College extends Component {
<div className="yslstatistic-base-item-labels">
{
shixun_time || shixun_time === 0 ?
<div className="yslstatistic-base-item-labelsp">{shixun_time}<span className="yslstatistic-base-item-labelsspan"></span></div>
<div className="yslstatistic-base-item-labelsp">{shixun_time}<span className="yslstatistic-base-item-labelsspan">分钟</span></div>
:
<Spin/>
}

@ -38,6 +38,36 @@ export function getNextHalfHourOfMoment(moment) {
return moment
}
 export function formatSeconds(value) {
        var theTime = parseInt(value);// 秒
        var middle= 0;// 分
        var hour= 0;// 小时
    
        if(theTime > 60) {
            middle= parseInt(theTime/60);
            theTime = parseInt(theTime%60);
            if(middle> 60) {
                hour= parseInt(middle/60);
                middle= parseInt(middle%60);
            }
        }
        var result = ""+parseInt(theTime)+"秒";
        if(middle > 0) {
if(hour>0){
result = ""+parseInt(middle)+"分";
}else{
result = ""+parseInt(middle)+"分"+result;
}
            
        }
        if(hour> 0) {
            result = ""+parseInt(hour)+"小时"+result;
        }
        return result;
    }
export function formatDuring(mss){
var days = parseInt(mss / (1000 * 60 * 60 * 24));
var hours = parseInt((mss % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));

@ -21,9 +21,9 @@ class LoadingSpin extends Component{
}
`}
</style>
<Spin tip="正在获取相关数据..."/>
<Spin/>
</div>
)
}
}
export default LoadingSpin;
export default LoadingSpin;

@ -20,7 +20,7 @@ export { bytesToSize as bytesToSize } from './UnitUtil';
export { markdownToHTML, uploadNameSizeSeperator, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll, isImageExtension,
downloadFile, sortDirections } from './TextUtil'
export { handleDateString, getNextHalfHourOfMoment,formatDuring } from './DateUtil'
export { handleDateString, getNextHalfHourOfMoment,formatDuring,formatSeconds} from './DateUtil'
export { configShareForIndex, configShareForPaths, configShareForShixuns, configShareForCourses, configShareForCustom } from './util/ShareUtil'

@ -0,0 +1,32 @@
import React, { useState } from 'react'
import './index.scss'
function noop() { }
export default ({ current, defaultCurrent, total, pageSize, onChange = noop }) => {
const maxPage = Math.ceil(total / pageSize)
const [page, setPage] = useState(current || defaultCurrent)
function next() {
if (page < maxPage) {
let value = page + 1
setPage(value)
onChange(value)
}
}
function prev() {
if (page > 1) {
let value = page - 1
setPage(value)
onChange(value)
}
}
return (
<div className="mini-pagination">
<a class={page === 1 ? 'disabled' : 'normal'} onClick={prev}>上一页</a>
<a class={page === maxPage ? 'disabled' : 'normal'} onClick={next} >下一页</a>
</div>
)
}

@ -0,0 +1,65 @@
.mini-pagination {
display: flex;
flex-flow: row nowrap;
justify-content: center;
align-items: center;
a {
display: block;
padding: 0 10px 0 22px;
border-width: 1px;
border-radius: 3px;
margin-right: 4px;
font-size: 12px;
line-height: 30px;
cursor: pointer;
border-style: solid;
outline: none;
border-color: #c4c6cf;
background: #fff;
color: #333;
position: relative;
&:hover {
background-color: #f2f3f7;
border-color: #a0a2ad;
text-decoration: none;
}
&:before {
position: absolute;
content: ' ';
width: 8px;
top: 10px;
left: 10px;
height: 8px;
transform: rotate(-45deg);
border-top: 1px solid #333;
border-left: 1px solid #333;
}
&:last-child {
padding: 0 22px 0 10px;
margin: 0 0 0 4px;
&:before {
left: auto;
right: 10px;
transform: rotate(135deg);
}
}
&.disabled {
cursor: not-allowed;
background-color: #f7f8fa;
border-color: #e6e7eb;
color: #e0e0e0;
&:before {
border-top: 1px solid #e0e0e0;
border-left: 1px solid #e0e0e0;
}
}
}
}

@ -0,0 +1,117 @@
import * as monaco from 'monaco-editor'
import { union } from 'lodash'
const ifelse = {
label: 'ifelse',
kind: monaco.languages.CompletionItemKind.Snippet,
insertText: [
'if (${1:condition}) {',
'\t$0',
'} else {',
'\t',
'}'
].join('\n'),
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'If-Else Statement'
}
function getWordsInString(string) {
return string.match(/[A-z]+/g)
}
const cArray = ['auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do', 'double', 'else', 'enum', 'extern',
'float', 'for', 'goto', 'if', 'int', 'long', 'register', 'return', 'short', 'signed', 'sizeof', 'static', 'struct',
'switch', 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while', 'inline', 'restrict', '_Bool', '_Complex',
'_Imaginary', '_Alignas', '_Alignof', '_Atomic', '_Static_assert', '_Noreturn', '_Thread_local', '_Generic']
monaco.languages.registerCompletionItemProvider('cpp', {
provideCompletionItems: (model) => {
const currentFileWords = getWordsInString(model.getValue());
const all = union(cArray, currentFileWords)
var suggestions = all.map((item) => {
return {
label: item,
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: item,
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet
}
});
suggestions.push(ifelse)
return { suggestions: suggestions };
}
});
// https://www.programiz.com/python-programming/keyword-list
const pythonArray = ['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif',
'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or',
'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
monaco.languages.registerCompletionItemProvider('python', {
provideCompletionItems: (model) => {
const currentFileWords = getWordsInString(model.getValue());
const all = union(pythonArray, currentFileWords)
var suggestions = all.map((item) => {
return {
label: item,
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: item,
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet
}
});
suggestions.push({
label: 'print',
kind: monaco.languages.CompletionItemKind.Snippet,
insertText: [
'print($0)',
].join('\n'),
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'print'
})
return { suggestions: suggestions };
}
});
const javaArray = ['abstract', 'assert', 'boolean', 'break', 'byte', 'case', 'catch', 'char', 'class', 'const',
'continue', 'default', 'do', 'double', 'else', 'enum', 'extends', 'final', 'finally', 'float', 'for', 'goto', 'if',
'implements', 'import', 'instance of', 'int', 'interface', 'long', 'native',
'new', 'package', 'private', 'protected', 'public', 'return', 'strictfp', 'short', 'static', 'super', 'switch',
'synchronized', 'this', 'throw', 'throws', 'transient', 'try', 'void', 'volatile', 'while']
monaco.languages.registerCompletionItemProvider('java', {
provideCompletionItems: (model) => {
const currentFileWords = getWordsInString(model.getValue());
const all = _.union(javaArray, currentFileWords)
var suggestions = all.map((item) => {
return {
label: item,
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: item,
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet
}
});
suggestions.push(ifelse)
suggestions.push({
label: 'main',
kind: monaco.languages.CompletionItemKind.Snippet,
insertText: [
'public static void main(String[] args) {',
'\t$0',
'}',
].join('\n'),
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'main function'
})
suggestions.push({
label: 'System.out.print',
kind: monaco.languages.CompletionItemKind.Snippet,
insertText: [
'System.out.print($0)',
].join('\n'),
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'system print'
})
return { suggestions: suggestions };
}
});

@ -0,0 +1,438 @@
.monaco-editor .minimap-slider,
.monaco-editor .minimap-slider .minimap-slider-horizontal {
background: rgba(121, 121, 121, 0.2);
}
.monaco-editor .minimap-slider:hover,
.monaco-editor .minimap-slider:hover .minimap-slider-horizontal {
background: rgba(100, 100, 100, 0.35);
}
.monaco-editor .minimap-slider.active,
.monaco-editor .minimap-slider.active .minimap-slider-horizontal {
background: rgba(191, 191, 191, 0.2);
}
.monaco-editor .minimap-shadow-visible {
box-shadow: #000000 -6px 0 6px -6px inset;
}
.monaco-editor .scroll-decoration {
box-shadow: #000000 0 6px 6px -6px inset;
}
.monaco-editor .focused .selected-text {
background-color: #264f78;
}
.monaco-editor .selected-text {
background-color: #3a3d41;
}
.monaco-editor,
.monaco-editor-background,
.monaco-editor .inputarea.ime-input {
background-color: #1e1e1e;
}
.monaco-editor,
.monaco-editor .inputarea.ime-input {
color: #d4d4d4;
}
.monaco-editor .margin {
background-color: #1e1e1e;
}
.monaco-editor .rangeHighlight {
background-color: rgba(255, 255, 255, 0.04);
}
.vs-whitespace {
color: rgba(227, 228, 226, 0.16) !important;
}
.monaco-editor .view-overlays .current-line {
border: 2px solid #282828;
}
.monaco-editor .margin-view-overlays .current-line-margin {
border: 2px solid #282828;
}
.monaco-editor .lines-content .cigr {
box-shadow: 1px 0 0 0 #404040 inset;
}
.monaco-editor .lines-content .cigra {
box-shadow: 1px 0 0 0 #707070 inset;
}
.monaco-editor .line-numbers {
color: #858585;
}
.monaco-editor .current-line~.line-numbers {
color: #c6c6c6;
}
.monaco-editor .view-ruler {
box-shadow: 1px 0 0 0 #5a5a5a inset;
}
.monaco-editor .cursor {
background-color: #aeafad;
border-color: #aeafad;
color: #515052;
}
.monaco-editor .squiggly-error {
background: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%206%203'%20enable-background%3D'new%200%200%206%203'%20height%3D'3'%20width%3D'6'%3E%3Cg%20fill%3D'%23ea4646'%3E%3Cpolygon%20points%3D'5.5%2C0%202.5%2C3%201.1%2C3%204.1%2C0'%2F%3E%3Cpolygon%20points%3D'4%2C0%206%2C2%206%2C0.6%205.4%2C0'%2F%3E%3Cpolygon%20points%3D'0%2C2%201%2C3%202.4%2C3%200%2C0.6'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E") repeat-x bottom left;
}
.monaco-editor .squiggly-warning {
background: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%206%203'%20enable-background%3D'new%200%200%206%203'%20height%3D'3'%20width%3D'6'%3E%3Cg%20fill%3D'%234d9e4d'%3E%3Cpolygon%20points%3D'5.5%2C0%202.5%2C3%201.1%2C3%204.1%2C0'%2F%3E%3Cpolygon%20points%3D'4%2C0%206%2C2%206%2C0.6%205.4%2C0'%2F%3E%3Cpolygon%20points%3D'0%2C2%201%2C3%202.4%2C3%200%2C0.6'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E") repeat-x bottom left;
}
.monaco-editor .squiggly-info {
background: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%206%203'%20enable-background%3D'new%200%200%206%203'%20height%3D'3'%20width%3D'6'%3E%3Cg%20fill%3D'%23008000'%3E%3Cpolygon%20points%3D'5.5%2C0%202.5%2C3%201.1%2C3%204.1%2C0'%2F%3E%3Cpolygon%20points%3D'4%2C0%206%2C2%206%2C0.6%205.4%2C0'%2F%3E%3Cpolygon%20points%3D'0%2C2%201%2C3%202.4%2C3%200%2C0.6'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E") repeat-x bottom left;
}
.monaco-editor .squiggly-hint {
background: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%223%22%20width%3D%2212%22%3E%3Cg%20fill%3D%22rgba(238%2C%20238%2C%20238%2C%200.7)%22%3E%3Ccircle%20cx%3D%221%22%20cy%3D%221%22%20r%3D%221%22%2F%3E%3Ccircle%20cx%3D%225%22%20cy%3D%221%22%20r%3D%221%22%2F%3E%3Ccircle%20cx%3D%229%22%20cy%3D%221%22%20r%3D%221%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E") no-repeat bottom left;
}
.showUnused .monaco-editor .squiggly-inline-unnecessary {
opacity: 0.667;
}
.monaco-diff-editor .diff-review-line-number {
color: #858585;
}
.monaco-diff-editor .diff-review-shadow {
box-shadow: #000000 0 -6px 6px -6px inset;
}
.monaco-editor .line-insert,
.monaco-editor .char-insert {
background-color: rgba(155, 185, 85, 0.2);
}
.monaco-diff-editor .line-insert,
.monaco-diff-editor .char-insert {
background-color: rgba(155, 185, 85, 0.2);
}
.monaco-editor .inline-added-margin-view-zone {
background-color: rgba(155, 185, 85, 0.2);
}
.monaco-editor .line-delete,
.monaco-editor .char-delete {
background-color: rgba(255, 0, 0, 0.2);
}
.monaco-diff-editor .line-delete,
.monaco-diff-editor .char-delete {
background-color: rgba(255, 0, 0, 0.2);
}
.monaco-editor .inline-deleted-margin-view-zone {
background-color: rgba(255, 0, 0, 0.2);
}
.monaco-diff-editor.side-by-side .editor.modified {
box-shadow: -6px 0 5px -5px #000000;
}
.monaco-editor .bracket-match {
background-color: rgba(0, 100, 0, 0.1);
}
.monaco-editor .bracket-match {
border: 1px solid #888888;
}
.monaco-editor .codelens-decoration {
color: #999999;
}
.monaco-editor .codelens-decoration>a:hover {
color: #4e94ce !important;
}
.monaco-editor .findOptionsWidget {
background-color: #252526;
}
.monaco-editor .findOptionsWidget {
box-shadow: 0 2px 8px #000000;
}
.monaco-editor .findMatch {
background-color: rgba(234, 92, 0, 0.33);
}
.monaco-editor .currentFindMatch {
background-color: #515c6a;
}
.monaco-editor .findScope {
background-color: rgba(58, 61, 65, 0.4);
}
.monaco-editor .find-widget {
background-color: #252526;
}
.monaco-editor .find-widget {
box-shadow: 0 2px 8px #000000;
}
.monaco-editor .find-widget.no-results .matchesCount {
color: #f48771;
}
.monaco-editor .find-widget .monaco-sash {
background-color: #454545;
width: 3px !important;
margin-left: -4px;
}
.monaco-editor .find-widget .monaco-checkbox .checkbox:checked+.label {
border: 1px solid #007acc;
}
.monaco-editor.vs .valueSetReplacement {
outline: solid 2px #888888;
}
.monaco-editor .detected-link-active {
color: #4e94ce !important;
}
.monaco-editor .monaco-editor-overlaymessage .anchor {
border-top-color: #007acc;
}
.monaco-editor .monaco-editor-overlaymessage .message {
border: 1px solid #007acc;
}
.monaco-editor .monaco-editor-overlaymessage .message {
background-color: #063b49;
}
.monaco-editor .parameter-hints-widget {
border: 1px solid #454545;
}
.monaco-editor .parameter-hints-widget.multiple .body {
border-left: 1px solid rgba(69, 69, 69, 0.5);
}
.monaco-editor .parameter-hints-widget .signature.has-docs {
border-bottom: 1px solid rgba(69, 69, 69, 0.5);
}
.monaco-editor .parameter-hints-widget {
background-color: #252526;
}
.monaco-editor .parameter-hints-widget a {
color: #3794ff;
}
.monaco-editor .parameter-hints-widget code {
background-color: rgba(10, 10, 10, 0.4);
}
.monaco-editor .snippet-placeholder {
background-color: rgba(124, 124, 124, 0.3);
outline-color: transparent;
}
.monaco-editor .finish-snippet-placeholder {
background-color: transparent;
outline-color: #525252;
}
.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-highlighted-label .highlight {
color: #0097fb;
}
.monaco-editor .suggest-widget {
color: #d4d4d4;
}
.monaco-editor .suggest-widget a {
color: #3794ff;
}
.monaco-editor .suggest-widget code {
background-color: rgba(10, 10, 10, 0.4);
}
.monaco-editor .focused .selectionHighlight {
background-color: rgba(173, 214, 255, 0.15);
}
.monaco-editor .selectionHighlight {
background-color: rgba(173, 214, 255, 0.07);
}
.monaco-editor .wordHighlight {
background-color: rgba(87, 87, 87, 0.72);
}
.monaco-editor .wordHighlightStrong {
background-color: rgba(0, 73, 114, 0.72);
}
.monaco-editor .accessibilityHelpWidget {
background-color: #252526;
}
.monaco-editor .accessibilityHelpWidget {
box-shadow: 0 2px 8px #000000;
}
.monaco-editor .tokens-inspect-widget {
border: 1px solid #454545;
}
.monaco-editor .tokens-inspect-widget .tokens-inspect-separator {
background-color: #454545;
}
.monaco-editor .tokens-inspect-widget {
background-color: #252526;
}
.monaco-editor .reference-zone-widget .ref-tree .referenceMatch {
background-color: rgba(234, 92, 0, 0.3);
}
.monaco-editor .reference-zone-widget .preview .reference-decoration {
background-color: rgba(255, 143, 0, 0.6);
}
.monaco-editor .reference-zone-widget .ref-tree {
background-color: #252526;
}
.monaco-editor .reference-zone-widget .ref-tree {
color: #bbbbbb;
}
.monaco-editor .reference-zone-widget .ref-tree .reference-file {
color: #ffffff;
}
.monaco-editor .reference-zone-widget .ref-tree .monaco-list:focus .monaco-list-rows>.monaco-list-row.selected:not(.highlighted) {
background-color: rgba(51, 153, 255, 0.2);
}
.monaco-editor .reference-zone-widget .ref-tree .monaco-list:focus .monaco-list-rows>.monaco-list-row.selected:not(.highlighted) {
color: #ffffff !important;
}
.monaco-editor .reference-zone-widget .preview .monaco-editor .monaco-editor-background,
.monaco-editor .reference-zone-widget .preview .monaco-editor .inputarea.ime-input {
background-color: #001f33;
}
.monaco-editor .reference-zone-widget .preview .monaco-editor .margin {
background-color: #001f33;
}
.monaco-editor .marker-widget a {
color: #3794ff;
}
.monaco-editor .hoverHighlight {
background-color: rgba(38, 79, 120, 0.25);
}
.monaco-editor .monaco-editor-hover {
background-color: #252526;
}
.monaco-editor .monaco-editor-hover {
border: 1px solid #454545;
}
.monaco-editor .monaco-editor-hover .hover-row:not(:first-child):not(:empty) {
border-top: 1px solid rgba(69, 69, 69, 0.5);
}
.monaco-editor .monaco-editor-hover hr {
border-top: 1px solid rgba(69, 69, 69, 0.5);
}
.monaco-editor .monaco-editor-hover hr {
border-bottom: 0px solid rgba(69, 69, 69, 0.5);
}
.monaco-editor .monaco-editor-hover a {
color: #3794ff;
}
.monaco-editor .monaco-editor-hover .hover-row .actions {
background-color: #2c2c2d;
}
.monaco-editor .monaco-editor-hover code {
background-color: rgba(10, 10, 10, 0.4);
}
.monaco-editor .goto-definition-link {
color: #4e94ce !important;
}
.mtki {
font-style: italic;
}
.mtkb {
font-weight: bold;
}
.mtku {
text-decoration: underline;
text-underline-position: under;
}
#extend-challenge-file-edit {
height: 100%;
}
/* context menu会有样式问题先给隐藏掉 */
.context-view {
display: none !important;
}
/* 去掉灰色边框 */
#extend-challenge-file-edit {
border: none !important;
}
/* 覆盖tpi样式 */
.monaco-tree .monaco-tree-rows>.monaco-tree-row>.content {
min-width: auto;
}
/* 选中行边框 */
.monaco-editor .view-overlays .current-line {
border-width: 1px !important;
}
#extend-challenge-file-edit {
width: '100%';
height: '100%';
border: '1px solid grey';
}

@ -0,0 +1,190 @@
import React, { useEffect, useRef, useState } from 'react'
import * as monaco from 'monaco-editor'
import './TPIMonacoConfig'
import './index.css'
function processSize(size) {
return !/^\d+$/.test(size) ? size : `${size}px`
}
function noop() { }
let __prevent_trigger_change_event = false
function debounce(func, wait, immediate) {
var timeout
return function () {
var context = this, args = arguments
var later = function () {
timeout = null
if (!immediate) func.apply(context, args)
};
var callNow = immediate && !timeout
clearTimeout(timeout)
timeout = setTimeout(later, wait)
if (callNow) func.apply(context, args)
}
}
const mirror2LanguageMap = {
'JFinal': 'java',
'Java': 'java',
'JavaWeb': 'java',
'Kotlin': 'java',
'Html': 'html',
'Css': 'css',
'Javascript': 'javascript',
'JavaScript': 'javascript',
'C/C++': 'cpp',
'MachineLearning': 'python',
'Python2.7': 'python',
'Python3.6': 'python',
'C#': 'csharp',
'R': 'r'
}
function getLanguageByMirrorName(mirror_name) {
let lang = 'javascript'
if (mirror_name && mirror_name.length) {
// htmlcsspythonhtmlcss
for (let i = mirror_name.length - 1; i >= 0; i--) {
let languageVal = mirror2LanguageMap[mirror_name[i]]
if (languageVal) {
lang = languageVal
break
}
}
}
return lang
}
export default ({
width = '100%',
height = '100%',
value,
language = 'javascript',
options = {},
overrideServices = {},
theme = 'vs-dark',
onChange = noop,
editorDidMount = noop
}) => {
const editorEl = useRef()
const editor = useRef({})
const [init, setInit] = useState(false)
function onLayout() {
if (window.ResizeObserver) {
const ro = new window.ResizeObserver(entries => {
for (let entry of entries) {
if (entry.target.offsetHeight > 0) {
editor.current.instance.layout()
}
}
})
ro.observe(editorEl.current.parentElement)
} else {
setTimeout(() => {
editor.current.instance.layout()
}, 100);
}
}
useEffect(() => {
editor.current.instance = monaco.editor.create(
editorEl.current, {
value,
language: getLanguageByMirrorName(language),
theme,
...options
},
overrideServices
)
editorDidMount(editor.current.instance, monaco)
editor.current.subscription = editor.current.instance.onDidChangeModelContent(event => {
if (!__prevent_trigger_change_event) {
onChange(editor.current.instance.getValue(), event);
}
})
setInit(true)
const resizeHandler = debounce(() => { editor.current.instance.layout() }, 100)
window.addEventListener('resize', resizeHandler)
onLayout()
return () => {
const el = editor.current.instance
el.dispose()
const model = el.getModel()
if (model) {
model.dispose()
}
if (editor.current.subscription) {
editor.current.subscription.dispose()
}
window.removeEventListener('resize', resizeHandler)
}
}, [editorEl.current])
useEffect(() => {
let instance = editor.current.instance
if (instance && init) {
__prevent_trigger_change_event = true
const model = instance.getModel()
instance.pushUndoStop()
model.pushEditOperations(
[],
[
{
range: model.getFullModelRange(),
text: value
}
]
)
instance.pushUndoStop();
__prevent_trigger_change_event = false;
}
}, [value, init])
useEffect(() => {
let instance = editor.current.instance
if (instance && init) {
monaco.editor.setModelLanguage(instance.getModel(), getLanguageByMirrorName(language))
}
}, [language, init])
useEffect(() => {
let instance = editor.current.instance
if (instance && init) {
monaco.editor.setTheme(theme)
}
}, [theme, init])
useEffect(() => {
let instance = editor.current.instance
if (instance && init) {
instance.updateOptions(options)
}
}, [options, init])
useEffect(() => {
let instance = editor.current.instance
if (instance && init) {
instance.layout()
}
}, [width, height, init])
const fixedWidth = processSize(width)
const fixedHeight = processSize(height)
const style = {
width: fixedWidth,
height: fixedHeight
}
return (
<div ref={editorEl} style={style} id="extend-challenge-file-edit" name="content" ></div>
)
}

@ -869,7 +869,7 @@ class CoursesIndex extends Component{
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
<Route path="/classrooms/:coursesId/shixun_homework/:homeworkid/settings"
<Route path="/classrooms/:coursesId/shixun_homework/:homeworkid/settings"
render={
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} {...common}/>)
}
@ -970,6 +970,13 @@ class CoursesIndex extends Component{
}
></Route>
{/* 主签到 */}
<Route path="/classrooms/:coursesId/attendances"
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} />)
}
></Route>
{/*/!*实训作业and课堂详情页*!/*/}
<Route path="/classrooms/:coursesId"
render={

@ -28,10 +28,15 @@ const TeacherList= Loadable({
loader: () => import('./members/teacherList'),
loading: Loading,
})
//主签到目录
const Signinmain= Loadable({
loader: () => import('./signin/mymain/Signinmain'),
loading: Loading,
});
//学生列表
const StudentsList= Loadable({
loader: () => import('./members/studentsList'),
loading: Loading,
loader: () => import('./members/studentsList'),
loading: Loading,
});
//分班列表
const CourseGroupList= Loadable({
@ -266,6 +271,7 @@ class ListPageIndex extends Component{
(props) => (<CourseVideo {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 教师列表*/}
<Route path="/classrooms/:coursesId/teachers"
render={
(props) => (<TeacherList updatabanners={()=>this.updatabanners()} {...this.props} {...props} {...this.state} />)
@ -277,6 +283,13 @@ class ListPageIndex extends Component{
(props) => (<StudentsList {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 主签到 */}
<Route path="/classrooms/:coursesId/attendances"
render={
(props) => (<Signinmain {...this.props} {...props} {...this.state} />)
}
></Route>
<Route path="/classrooms/:coursesId/course_groups/:course_group_id"
render={
(props) => (<StudentsList {...this.props} {...props} {...this.state} />)

@ -191,15 +191,21 @@ class Video extends Component {
const CourseId = this.props.match.params.coursesId;
const VID = this.props.match.params.videoId;
const login = this.props.user && this.props.user.login;
let login = this.props && this.props.user&&this.props.user.login;;
const _inputValue = videoId && this.getCopyText(videoId.file_url, videoId.cover_url);
const { admin, is_teacher, business, user_id } = this.props.user;
// const { admin, is_teacher, business, user_id } = this.props && this.props.user;
let admin = this.props && this.props.user&&this.props.user.admin;;
let business = this.props && this.props.user&&this.props.user.business;;
let user_id = this.props && this.props.user&&this.props.user.user_id;;
const { videos, upload, uploadVideo, videoData, changePage, pageSize, page } = this.props;
const operation = admin || business;
const { course_identity } = this.props.coursedata;
const { course_identity } = this.props && this.props.coursedata;
const flagMove = parseInt(course_identity) < 5;
return (
@ -240,8 +246,12 @@ class Video extends Component {
{
videos && videos.length > 0 ?
<React.Fragment>
<div style={{display:'flex'}}>
<p className="font-grey-9 mt20 mb20 pl5"> <span className="color-orange">{videoData && videoData.count}</span> </p>
<p className="mt20 mb20 pl5" style={{marginLeft:'auto',color:'#C0C4CC'}}>播放数据从2020-03-13 24:00开始统计</p>
</div>
<div className="videoContent">
{
videos.map((item, key) => {

@ -8,7 +8,7 @@ import Videos from './Video';
import Lives from './Live';
import LivesNew from './LiveNew';
import VideoLink from './VideoLink';
import Videostatistics from '../videostatistics/Videostatistics';
import './video.css';
import '../css/Courses.css';
import '../publicNav/nav.css';
@ -37,7 +37,8 @@ class VideoIndex extends Component{
liveId:undefined,
liveVisible:false
liveVisible:false,
statistics:false,
}
}
@ -101,7 +102,7 @@ class VideoIndex extends Component{
console.log(error);
})
}
// 获取视频列表
getList=(page)=>{
const { coursesId , videoId }=this.props.match.params;
@ -133,7 +134,7 @@ class VideoIndex extends Component{
page:1
})
this.checkType(e.key,1);
}
}
changePage=(page,type)=>{
this.setState({
@ -153,7 +154,7 @@ class VideoIndex extends Component{
this.setVisible(true);
}
uploadVideo=(upload)=>{
this.setState({
upload,
isSpining:true
@ -234,12 +235,17 @@ class VideoIndex extends Component{
this.getList(page);
}
}
//统计
statisticsy=(bool)=>{
this.setState({
statistics:bool
})
}
render(){
const { videos , upload , videoData , type , liveData , lives , page , liveVisible , isSpining , liveId , otherLinkVisible } = this.state;
const { admin , is_teacher , business } = this.props.user;
const { videos , upload , videoData , type , liveData , lives , page , liveVisible , isSpining , liveId , otherLinkVisible,statistics } = this.state;
const { coursesId , videoId }=this.props.match.params;
const {course_identity} = this.props.coursedata;
let course_identity = this.props&&this.props.coursedata;
let is_teacher=this.props&&this.props.user&&this.props.user.is_teacher;
const flag = parseInt(course_identity) < 5;
const newOperation = flag;
const new_upload = flag && (is_teacher && this.props.checkIfProfessionalCertification());
@ -252,11 +258,11 @@ class VideoIndex extends Component{
notification={this.props.showNotification}
setVisible={this.setLinkeVisible}
></VideoLink>
<LivesNew
visible={liveVisible}
<LivesNew
visible={liveVisible}
liveId={liveId}
setliveVisibel={this.setliveVisibel}
{...this.props}
setliveVisibel={this.setliveVisibel}
{...this.props}
{...this.state}
></LivesNew>
{
@ -278,67 +284,77 @@ class VideoIndex extends Component{
}
`}</style>
}
<div className="edu-back-white" style={{marginBottom:"1px"}}>
{
statistics===false?
<div className="edu-back-white" style={{marginBottom:"1px"}}>
<div className="clearfix pl30 pr30 menuDiv">
{
videoData && videoData.category_name && type === "video" ?
<span className="font-18 fl color-dark-21 mt20 mb20">{videoData.category_name}</span>
:
<div className="task_menu_ul fl mt2" style={{width:"400px"}}>
<Menu mode="horizontal" selectedKeys={[type]} onClick={this.changeType}>
<Menu.Item key="video">视频</Menu.Item>
<Menu.Item key="live">直播</Menu.Item>
</Menu>
</div>
}
<li className="fr mt20 mb20">
{
type === "video" ?
<React.Fragment>
{
newOperation ?
<span>
{
videoId ?
<WordsBtn style="blue" onClick={()=>this.editDir(videoData && videoData.category_name,videoId)} className={"ml30 font-16"}>目录重命名</WordsBtn>
:
<WordsBtn style="blue" className="ml30 font-16" onClick={this.addDir}>新建目录</WordsBtn>
}
<WordsBtn style="blue" className="ml30 font-16" onClick={()=>this.setLinkeVisible(true)}>增加外链</WordsBtn>
<div className="clearfix pl30 pr30 menuDiv">
{
videoData && videoData.category_name && type === "video" ?
<span className="font-18 fl color-dark-21 mt20 mb20">{videoData.category_name}</span>
:
<div className="task_menu_ul fl mt2" style={{width:"240px"}}>
<Menu mode="horizontal" selectedKeys={[type]} onClick={this.changeType}>
<Menu.Item key="video">视频</Menu.Item>
<Menu.Item key="live">直播</Menu.Item>
</Menu>
</div>
}
<li className="fr mt20 mb20">
{
type === "video" ?
<React.Fragment>
{
newOperation ?
<span>
<WordsBtn style="blue" className="ml30 font-16 tongjis"
onClick={()=>this.statisticsy(true)}
><i className="iconfont icon-tongji1 mr5"></i></WordsBtn>
{
videoId ?
<WordsBtn style="blue" onClick={()=>this.editDir(videoData && videoData.category_name,videoId)} className={"ml30 font-16"}>目录重命名</WordsBtn>
:
<WordsBtn style="blue" className="ml30 font-16" onClick={this.addDir}>新建目录</WordsBtn>
}
<WordsBtn style="blue" className="ml30 font-16" onClick={()=>this.setLinkeVisible(true)}>增加外链</WordsBtn>
</span>:""
}
{
new_upload ?
<span>
}
{
new_upload ?
<span>
{
upload ?
<WordsBtn style="grey" className="font-16 ml30" onClick={()=>this.uploadVideo(false)}>取消</WordsBtn>
:
<WordsBtn style="blue" className="font-16 ml30" onClick={this.toUpload}>上传视频</WordsBtn>
}
upload ?
<WordsBtn style="grey" className="font-16 ml30" onClick={()=>this.uploadVideo(false)}>取消</WordsBtn>
:
<WordsBtn style="blue" className="font-16 ml30" onClick={this.toUpload}>上传视频</WordsBtn>
}
</span>:""
}
</React.Fragment>
:
<WordsBtn style="blue" className="font-16 ml30" onClick={this.liveSetting}>添加直播</WordsBtn>
}
</li>
</div>
</div>
}
</React.Fragment>
:
<WordsBtn style="blue" className="font-16 ml30" onClick={this.liveSetting}>添加直播</WordsBtn>
}
</li>
</div>
</div>
:
<Videostatistics {...this.props} {...this.state} statisticsy={(b)=>this.statisticsy(b)}></Videostatistics>
}
{
statistics===false?
<Spin spinning={isSpining}>
{
type === "video" ?
<Videos
upload={upload}
videos={videos}
type === "video" ?
<Videos
upload={upload}
videos={videos}
page={page}
data={videoData}
pageSize={PAGE_SIZE}
uploadVideo={this.uploadVideo}
listFunc={this.getList}
changePage={this.changePage}
{...this.props}
{...this.props}
{...this.state}
></Videos>
:
@ -355,6 +371,9 @@ class VideoIndex extends Component{
></Lives>
}
</Spin>
:
""
}
</React.Fragment>
)
}

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

@ -176,4 +176,14 @@
}
.wei_meet_info{
margin:0px auto;
}
}
.tongjis{
border: 1px solid #4CACFF;
padding-left: 10px;
padding-right: 10px;
padding-top: 5px;
padding-bottom: 5px;
border-radius: 5px;
line-height: 1px;
}

@ -14,9 +14,9 @@ function CommonWorkAppraiseReviseAttachments(props) {
}
`}</style>
<div className={"color-grey-6 mb10 font-16"}>
补交附件
补交内容
</div>
{/* {age} */}
<div className={"ml20"}>
@ -47,6 +47,6 @@ function CommonWorkAppraiseReviseAttachments(props) {
</div>}
</React.Fragment>
)
}
export default CommonWorkAppraiseReviseAttachments;

@ -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 && <a href="javascript:void(0)" className="color-grey-9" onClick={this.homeworkstart}>{ showdatatypes ? "立即发布" : "立即截止" }</a> }
</div>

@ -89,7 +89,6 @@ class CoursesBanner extends Component {
}
HideAddcoursestypess=(i)=>{
console.log("调用了");
this.setState({
Addcoursestypes:false,
mydisplay:true,

@ -1242,7 +1242,8 @@ class Coursesleftnav extends Component{
item.type==="board"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-taolun mr10 fl":"iconfont icon-taolun mr10 fl"} ></i>:
item.type==="course_group"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-fenban mr10 fl":"iconfont icon-fenban mr10 fl"} ></i>:
item.type==="statistics"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-tongji mr10 fl":"iconfont icon-tongji mr10 fl"} ></i>:
item.type==="video"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-bofang2 mr10 fl":"iconfont icon-bofang2 mr10 fl"} ></i>:""
item.type==="attendance"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-qiandao-1 mr10 fl":"iconfont icon-qiandao-1 mr10 fl"} ></i>:
item.type==="video"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-bofang2 mr10 fl":"iconfont icon-bofang2 mr10 fl"} ></i>:""
}
{/*||this.props.location.pathname===this.state.url&&key===this.state.indexs*/}
@ -1431,7 +1432,7 @@ class Coursesleftnav extends Component{
item.type==="attachment"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-ziyuan mr10 fl":"iconfont icon-ziyuan mr10 fl"} ></i>:
item.type==="board"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-taolun mr10 fl":"iconfont icon-taolun mr10 fl"} ></i>:
item.type==="course_group"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-fenban mr10 fl":"iconfont icon-fenban mr10 fl"} ></i>:
item.type==="attendance"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-qiandao-1 mr10 fl":"iconfont icon-qiandao-1 mr10 fl"} ></i>:
item.type==="video"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-bofang2 mr10 fl":"iconfont icon-bofang2 mr10 fl"} ></i>:
item.type==="statistics"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-tongji mr10 fl":"iconfont icon-tongji mr10 fl"} ></i>:""
}

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

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

@ -295,7 +295,10 @@ class Studentshavecompletedthelist extends Component {
)
},
{
title: '最终成绩',
title:<span>最终成绩<Tooltip placement="top" title={<pre>
鼠标停留具体分值上可查<br/>
看得分明细<br/>
</pre>}><img src={getImageUrl("images/educoder/problem.png")} className={"ml2"}/></Tooltip></span>,
dataIndex: 'efficiencyscore',
key: 'efficiencyscore',
align: 'center',
@ -337,6 +340,22 @@ class Studentshavecompletedthelist extends Component {
}}>{record.efficiencyscore}</span>
</Tooltip>
:
<Tooltip placement="right" title={<div>
<div>{record.objective_score===undefined?"":record.objective_score === "--" ? <span>客观题得分0</span> :
<span>客观题得分{record.objective_score}</span>}
</div>
<div>{record.subjective_score===undefined?"":record.subjective_score === "--" ? <span>主观题得分0</span> :
<span>主观题得分{record.subjective_score}</span>}
</div>
<div>{record.efficiencyscore === "--" ? <span>最终成绩0</span> :
<span>最终成绩{record.efficiencyscore}</span>}
</div>
</div>
}>
<span style={parseInt(record.efficiencyscore) > 90 ? {
color: '#DD1717',
textAlign: "center"
@ -350,6 +369,7 @@ class Studentshavecompletedthelist extends Component {
color: '#747A7F',
textAlign: "center"
}}>{record.efficiencyscore}</span>
</Tooltip>
}
</span>
)
@ -587,7 +607,10 @@ class Studentshavecompletedthelist extends Component {
)
},
{
title: '最终成绩',
title:<span>最终成绩<Tooltip placement="top" title={<pre>
鼠标停留具体分值上可查<br/>
看得分明细<br/>
</pre>}><img src={getImageUrl("images/educoder/problem.png")} className={"ml2"}/></Tooltip></span>,
dataIndex: 'efficiencyscore',
key: 'efficiencyscore',
align: 'center',
@ -631,6 +654,22 @@ class Studentshavecompletedthelist extends Component {
}}>{record.efficiencyscore}</span>
</Tooltip>
:
<Tooltip placement="right" title={<div>
<div>{record.objective_score===undefined?"":record.objective_score === "--" ? <span>客观题得分0</span> :
<span>客观题得分{record.objective_score}</span>}
</div>
<div>{record.subjective_score===undefined?"":record.subjective_score === "--" ? <span>主观题得分0</span> :
<span>主观题得分{record.subjective_score}</span>}
</div>
<div>{record.efficiencyscore === "--" ? <span>最终成绩0</span> :
<span>最终成绩{record.efficiencyscore}</span>}
</div>
</div>
}>
<span style={parseInt(record.efficiencyscore) > 90 ? {
color: '#DD1717',
textAlign: "center",
@ -648,6 +687,7 @@ class Studentshavecompletedthelist extends Component {
textAlign: "center",
width:"199px"
}}>{record.efficiencyscore}</span>
</Tooltip>
}
</span>
@ -871,7 +911,10 @@ class Studentshavecompletedthelist extends Component {
)
},
{
title: '最终成绩',
title: <span>最终成绩<Tooltip placement="top" title={<pre>
鼠标停留具体分值上可查<br/>
看得分明细<br/>
</pre>}><img src={getImageUrl("images/educoder/problem.png")} className={"ml2"}/></Tooltip></span>,
dataIndex: 'efficiencyscore',
key: 'efficiencyscore',
align: 'center',
@ -909,6 +952,22 @@ class Studentshavecompletedthelist extends Component {
}}>{record.efficiencyscore}</span>
</Tooltip>
:
<Tooltip placement="right" title={<div>
<div>{record.objective_score===undefined?"":record.objective_score === "--" ? <span>客观题得分0</span> :
<span>客观题得分{record.objective_score}</span>}
</div>
<div>{record.subjective_score===undefined?"":record.subjective_score === "--" ? <span>主观题得分0</span> :
<span>主观题得分{record.subjective_score}</span>}
</div>
<div>{record.efficiencyscore === "--" ? <span>最终成绩0</span> :
<span>最终成绩{record.efficiencyscore}</span>}
</div>
</div>
}>
<span style={parseInt(record.efficiencyscore) > 90 ? {
color: '#DD1717',
textAlign: "center",
@ -922,6 +981,7 @@ class Studentshavecompletedthelist extends Component {
color: '#747A7F',
textAlign: "center",
}}>{record.efficiencyscore}</span>
</Tooltip>
}
</span>
)
@ -1095,7 +1155,10 @@ class Studentshavecompletedthelist extends Component {
)
},
{
title: '最终成绩',
title: <span>最终成绩<Tooltip placement="top" title={<pre>
鼠标停留具体分值上可查<br/>
看得分明细<br/>
</pre>}><img src={getImageUrl("images/educoder/problem.png")} className={"ml2"}/></Tooltip></span>,
dataIndex: 'efficiencyscore',
key: 'efficiencyscore',
align: 'center',
@ -1132,6 +1195,22 @@ class Studentshavecompletedthelist extends Component {
}}>{record.efficiencyscore}</span>
</Tooltip>
:
<Tooltip placement="right" title={<div>
<div>{record.objective_score===undefined?"":record.objective_score === "--" ? <span>客观题得分0</span> :
<span>客观题得分{record.objective_score}</span>}
</div>
<div>{record.subjective_score===undefined?"":record.subjective_score === "--" ? <span>主观题得分0</span> :
<span>主观题得分{record.subjective_score}</span>}
</div>
<div>{record.efficiencyscore === "--" ? <span>最终成绩0</span> :
<span>最终成绩{record.efficiencyscore}</span>}
</div>
</div>
}>
<span style={parseInt(record.efficiencyscore) > 90 ? {
color: '#DD1717',
textAlign: "center",
@ -1145,6 +1224,7 @@ class Studentshavecompletedthelist extends Component {
color: '#747A7F',
textAlign: "center",
}}>{record.efficiencyscore}</span>
</Tooltip>
}
</span>
)
@ -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

@ -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)}
></DMDEditor>
</div>
{!exerciseIsPublish &&
{!exerciseIsPublish &&
<React.Fragment>
{itemIndex != 0 && <Tooltip title="删除">
<i className="iconfont icon-htmal5icon19 font-18 color-grey-c ml10"
<i className="iconfont icon-htmal5icon19 font-18 color-grey-c ml10"
onClick={() => this.props.deleteChildAnswer(index, itemIndex)}
style={{float: 'right'}}
></i>
</Tooltip> }
{itemIndex === 0 && <Tooltip title="删除">
<i className="iconfont icon-htmal5icon19 font-18 color-grey-c ml10"
onClick={() => this.props.deleteChildAnswermain(index, itemIndex)}
style={{float: 'right'}}
></i>
</Tooltip> }
{ <Tooltip title={`新增参考答案`}>
<i className="color-green font-16 iconfont icon-roundaddfill ml6"
onClick={() => addChildAnswer(index)}

@ -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{
<span className="color-grey-9 font-12 fl">客观题由系统自动评分允许手动调分请设置标准答案 支持最多5个空每空得分按照本题的总分平均计算</span>
</p>
<NullMDEditor {...this.props} mdID={`question_${question_id}`} placeholder="请您输入题目" height={155}
<NullMDEditor {...this.props} mdID={`question_${question_id}`} placeholder="请您输入题目" height={155}
initValue={question_title} onChange={(val) => this.setState({ question_title: val})}
onPlaceholderChange={this.onPlaceholderChange} showNullButton={exerciseIsPublish ? false : true}
ref="titleEditor"
></NullMDEditor>
<div className="clearfix">
{
standard_answers.map((answers, index) => {
return <NullChildEditor
return <NullChildEditor
ref={`nullChildEditor${index}`}
{...this.props}
toMDMode={this.toMDMode}
@ -321,6 +336,7 @@ class NullEditor extends Component{
onAnswerChange={this.onAnswerChange}
addChildAnswer={this.addChildAnswer}
deleteChildAnswer={this.deleteChildAnswer}
deleteChildAnswermain={this.deleteChildAnswermain}
></NullChildEditor>
// answer.map((item, itemIndex) => {
// return <DMDEditor
@ -331,7 +347,7 @@ class NullEditor extends Component{
// })
})
}
</div>
<div className="clearfix ">
@ -346,16 +362,16 @@ class NullEditor extends Component{
<InputNumber step={0.1} precision={1} min={0} max={100} style={{width: 100}} value={question_score} onChange={this.on_question_score_change}
disabled={exerciseIsPublish} placeholder="请填写分数"
></InputNumber >
<span className="fr">
<ActionBtn style="greyBack" className="middle mr20" onClick={this.onCancel}
>取消</ActionBtn>
<ActionBtn style="blue" className="middle" onClick={this.onSave}>保存</ActionBtn>
</span>
</div>
</div>
)
}

@ -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{
<span className="mr10 lineh-35 font-16">答案填空{key+1}:</span>
<div className="flex1" style={{width:"0"}}>
{
user_exercise_status == 1 ?
<input value={item.value} className="input-100-35" style={{backgroundColor: "#F5F5F5",cursor:"default"}} placeholder={ isStudent && item.value ? `请输入填空${key+1}的答案` : "" } readOnly/>
user_exercise_status == 1 ?
// <input value={item.value} className="input-100-35" style={{backgroundColor: "#F5F5F5",cursor:"default"}} placeholder={ isStudent && item.value ? `请输入填空${key+1}的答案` : "" } readOnly/>
<div id="content_editorMd_show" className="new_li content_editorMd_show markdown-body"
// style={_style}
style={{backgroundColor: "#F5F5F5",cursor:"default"}}
dangerouslySetInnerHTML={{__html: markdownToHTML(item.value)}}
// onClick={this.toMDMode}
>
</div>
:
<DMDEditor
<DMDEditor
ref={`md${questionType.q_position}${key}`}
toMDMode={this.toMDMode} toShowMode={this.toShowMode}
height={150} className={'optionMdEditor'} watch={false} noStorage={true}
@ -129,7 +137,7 @@ class fillEmpty extends Component{
}
{
// 答案公开,且试卷已经截止
questionType.standard_answer &&
questionType.standard_answer &&
<div>
<p className="bor-top-greyE pt20 mt20 font-16 mb10">参考答案</p>
{ 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(
<MarkdownToHtml content={i} selector={'empty_' + (this.props.index + 1) +(k + 1) + (index + 1)}
className="standardAnswer answerStyle mb10"
className="standardAnswer answerStyle mb10"
></MarkdownToHtml>
// <div className="standardAnswer markdown-body answerStyle mb10" dangerouslySetInnerHTML={{__html: markdownToHTML1(i)}}></div>
)
@ -154,7 +162,7 @@ class fillEmpty extends Component{
}
</div>
}
</div>
)
}

@ -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"
],
})(
<Checkbox.Group style={{ marginTop: "10px"}}>
@ -949,6 +949,7 @@ class CoursesNew extends Component {
<Checkbox value={"board"} className="fl">讨论</Checkbox>
<Checkbox value={"course_group"} className="fl">分班</Checkbox>
<Checkbox value={"statistics"} className="fl">统计</Checkbox>
<Checkbox value={"attendance"} className="fl">签到</Checkbox>
</Checkbox.Group>
)}
</Form.Item>

@ -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"
],
})(
<Checkbox.Group style={{ marginTop: "10px"}}>
@ -1003,6 +1003,7 @@ class Goldsubject extends Component {
<Checkbox value={"board"} className="fl">讨论</Checkbox>
<Checkbox value={"course_group"} className="fl">分班</Checkbox>
<Checkbox value={"statistics"} className="fl">统计</Checkbox>
<Checkbox value={"attendance"} className="fl">签到</Checkbox>
</Checkbox.Group>
)}
</Form.Item>

@ -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(
<div className="bor-top-greyE pt20">
<p className="clearfix mb10">

@ -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 === "--" ? "--" :
<a style={
<span style={
{
color: '#747A7F',
textAlign: "center"
}
}
>{record.cost_time === null ? "--" : record.cost_time === undefined ? "--" : record.cost_time}
</a>
</span>
}
</span>
// <a style={
@ -1204,7 +1204,10 @@ class Listofworksstudentone extends Component {
)
},
{
title: '当前成绩',
title: <span>当前成绩<Tooltip placement="top"title={<pre>
鼠标停留具体分值上可查<br/>
看得分明细<br/>
</pre>}><img src={getImageUrl("images/educoder/problem.png")} className={"ml2"}/></Tooltip></span>,
dataIndex: 'work_score',
key: 'work_score',
align: 'center',
@ -1300,7 +1303,7 @@ class Listofworksstudentone extends Component {
// onClick={() => this.Viewstudenttraininginformationt(record)}
>{record.has_comment===true?"详情":"评阅 "}</a> :
<span>
<a style={{textAlign: "center"}} className="color-blue maxnamewidth120"
<a style={{textAlign: "center"}} className="color-blue"
onMouseDown={(e) => this.Viewstudenttraininginformationtysl2(e, record)}
// onClick={() => this.Viewstudenttraininginformationt(record)}
>{record.has_comment===true?"详情":"评阅 "}</a>
@ -1446,14 +1449,14 @@ class Listofworksstudentone extends Component {
}
}>
{record.cost_time === null ? "--" : record.cost_time === undefined ? "--" : record.cost_time === "--" ? "--" :
<a style={
<span style={
{
color: '#747A7F',
textAlign: "center"
}
}
>{record.cost_time === null ? "--" : record.cost_time === undefined ? "--" : record.cost_time}
</a>
</span>
}
</span>
)
@ -1569,7 +1572,10 @@ class Listofworksstudentone extends Component {
)
},
{
title: '当前成绩',
title: <span>当前成绩<Tooltip placement="top" title={<pre>
鼠标停留具体分值上可查<br/>
看得分明细<br/>
</pre>}><img src={getImageUrl("images/educoder/problem.png")} className={"ml2"}/></Tooltip></span>,
dataIndex: 'work_score',
key: 'work_score',
align: 'center',

@ -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}
/> : ""}
<Modals

@ -0,0 +1,296 @@
import "../css/Signinstatistics.css";
import React,{ Component } from "react";
import { Row, Col,Card,Select} from 'antd';
import {getImageUrl} from 'educoder';
import axios from 'axios';
import NoneDatas from "../component/NoneDatas";
import {
Chart,
Geom,
Axis,
Tooltip,
} from "bizcharts";
import LoadingSpin from "../../../../common/LoadingSpin";
const { Option } = Select;
class Signinstatistics extends Component {
constructor(props) {
super(props)
this.state={
datas:null,
newlist:undefined,
course_groups:[{id:"全部",name:"全部"}],
spal:false,
}
}
getdata=(group_id)=>{
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(
<React.Fragment >
<div>
{
spal===true?
<div style={{
minHeight:"500px",
}}>
<LoadingSpin></LoadingSpin>
</div>
:
<div>
<Row type="flex" justify="space-between" className={"mt20"}>
<style>
{
`
.lishiqiandao{
background-image: url(${getImageUrl(`images/qiandao/lishi.png`)});
}
.daokeqiandao{
background-image: url(${getImageUrl(`images/qiandao/daoke.png`)});
}
.kuangkeqiandao{
background-image: url(${getImageUrl(`images/qiandao/kuangke.png`)});
}
.qingjiaqiandao{
background-image: url(${getImageUrl(`images/qiandao/qingjia.png`)});
}
`
}
</style>
<Col span={6}>
<Card style={{ width: 209 }} className={"gutterrowbox lishiqiandao"}>
<div className={"gutterrowboxcontent ml5"}>{datas&&datas.all_history_count}</div>
</Card>
</Col>
<Col span={6}>
<Card style={{ width: 209 }} className={"ml8 gutterrowbox daokeqiandao"}>
<div className={"gutterrowboxcontent ml5"}>{datas?(datas&&datas.avg_normal_rate*100).toFixed(0)+"%":""}</div>
</Card>
</Col>
<Col span={6}>
<Card style={{ width: 209 }} className={"ml14 gutterrowbox kuangkeqiandao"}>
<div className={"gutterrowboxcontent ml5"}>{datas?(datas&&datas.avg_absence_rate*100).toFixed(0)+"%":""}</div>
</Card>
</Col>
<Col span={6} >
<Card style={{ width: 209 }} className={"ml20 gutterrowbox qingjiaqiandao"}>
<div className={"gutterrowboxcontent ml5"}>{datas?(datas&&datas.avg_leave_rate*100).toFixed(0)+"%":""}</div>
</Card>
</Col>
</Row>
{newlist&&newlist.length>0?<div className={"SigninstatisticsChart mt20"}>
<div className={"pd30"}>
<Row>
<Col span={14}>
<Row type="flex" justify="start">
<Col span={5}>
<Row>
<Col span={12} className={"mindaoke mr5"}></Col>
<Col>到课率</Col>
</Row>
</Col>
<Col span={5}>
<Row>
<Col span={12} className={"minkuangke mr5"}></Col>
<Col>旷课率</Col>
</Row>
</Col>
<Col span={5}>
<Row>
<Col span={12} className={"minqingjia mr5"}></Col>
<Col>请假率</Col>
</Row>
</Col>
</Row>
</Col>
<Col span={10}><Row type="flex" justify="end">
<Col span={8} className={"Signinstatisticsfont"}>显示最近十次签到</Col>
<Col span={10}>
<Select defaultValue={"全部"} onChange={(e)=>this.handleChange(e)} style={{width:"130px"}} className={"Signinstatisticsfontselect"}>
{course_groups&&course_groups.map((item,key)=>{
return(
<Option value={item.id} title={item.name} >{item.name}</Option>
)
})}
</Select>
</Col>
</Row></Col>
</Row>
</div>
<div className={"padding03000"}>
<Chart height={400} data={newlist} scale={cols} forceFit>
{/*<Legend />*/}
<Axis name="month" />
<Axis
name="temperature"
label={{
formatter: val => `${val}%`
}}
/>
<Tooltip
crosshairs={{
type: "y"
}}
/>
<Geom
type="line"
position="month*temperature"
size={2}
// color={"city"}
shape={"smooth"}
color={["city", ["#26C7C9", "#FF835C","#EDBA6F"]]}
tooltip={['name*temperature*city', (name, temperature,city) => {
return {
//自定义 tooltip 上显示的 title 显示内容等。
name: city,
title: name,
value: temperature+"%"
};
}]}
/>
<Geom
type="point"
position="month*temperature"
size={4}
shape={"circle"}
color={["city", ["#26C7C9", "#FF835C","#EDBA6F"]]}
style={{
stroke: "#fff",
lineWidth: 1
}}
// tooltip={['month*sold', (month, temperature) => {
// return {
// //自定义 tooltip 上显示的 title 显示内容等。
// name: 'sold1',
// title: 'dddd' + month,
// value: temperature
// };
// }]}
/>
</Chart>
</div>
</div>:<div style={{
minHeight:"400px",
}}>
<NoneDatas></NoneDatas>
</div>}
</div>
}
</div>
</React.Fragment>
)
}
}
export default Signinstatistics;

@ -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 (
<React.Fragment>
<div className="ws100s edu-back-white sortinxdirection" style={{
position: "relative"
}}>
<div className="ws100s teacherentrydiv ">
<p className="ws100s teachedivp ymaxnamewidthdivp">
{item && item.name}
</p>
<div className="ws100s sortinxdirection mt17">
<div className="teachedivps">
签到人数
</div>
<div className="progressivps">
<Progress percent={jdt} showInfo={false} strokeColor="#1890ff"/>
</div>
<div className="progressivpss h28s">
已签到 {item && item.normal_count ? item.normal_count : 0} /
应签到 {item && item.all_count ? item.all_count : 0}
</div>
</div>
<div className="ws100s sortinxdirection mt5">
<div className="ws75s sortinxdirection">
<div className="teachedivps h40s">
签到发起人
</div>
<div className="teachedivpsy ymaxnamewidth60 h40s">
{item && item.author.user_name}
</div>
<div className="teachedivps ml20 h40s">
签到方式
</div>
<div className="teachedivpsy ymaxnamewidth80 h40s">
{item && item.mode ?
item.mode === "QRCODE" ?
"二维码签到"
: item.mode === "NUMBER" ?
"数字签到"
: item.mode === "QUICK" ?
"快捷签到"
: ""
: ""}
</div>
<div className="teachedivps ml20 h40s">
开始结束时间
</div>
<div className="teachedivpsy h40s">
{item && item.attendance_date} {item && item.start_time}-{item && item.end_time}
</div>
</div>
{
defaultActiveKey === "1" ?
<div className="ws25s" style={item && item.mode === "NUMBER"?{
position: "relative",
overflow: 'hidden',
}:{
position: "relative",
}}>
{
item && item.mode === "NUMBER" ?
<div className="ws100s xaxisreverseorder ">
<p className="h40s font-16 xiaoshou" style={{
color: "#1890FF"
}} onClick={() => {
jsCopy("file_path" + 1)
}}>
复制签到码
</p>
<p className="h40s font-16" style={{
color: "#333333",
marginRight: "10px",
}}>
{item&&item.code}
</p>
<p className="h40s font-16" style={{
color: "#909399"
}}>签到码</p>
<div style={{
overflow: 'hidden',
height: "1px",
width: "1px",
position: "absolute",
right: "-200px",
}}>
<input id={"file_path" + 1} className="file_path_input" value={item&&item.code}/>
</div>
</div>
:
item && item.mode === "QRCODE" ?
<div style={{
position: "absolute",
bottom: "-11px",
right: "0px",
}}>
<QRCode
value={item&&item.code} //value参数为生成二维码的链接
size={84} //二维码的宽高尺寸
fgColor="#000000" //二维码的颜色
/>
<div className="font-14" style={{
width: "84px",
textAlign: "center",
color: "#333333"
}}>
扫码签到
</div>
</div>
: ""
}
</div>
:
""
}
</div>
</div>
</div>
</React.Fragment>
)
}
}
export default Detailss;

@ -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(
<div className="edu-tab-con-box clearfix edu-txt-center intermediatecenter" style={ style || { width:"100%",height:"100%"}}>
<style>
{`
.edu-tab-con-box{
padding:100px 0px;
}
.ant-modal-body .edu-tab-con-box{
padding:0px!important;
}
img.edu-nodata-img{
margin: 40px auto 20px;
}
.zenwuxgsj{
font-size:17px;
font-family:MicrosoftYaHei;
color:rgba(136,136,136,1);
}
`}
</style>
<img className="edu-nodata-img mb20" src={getUrl("/images/educoder/nodata.png")}/>
<p className="edu-nodata-p mb10 zenwuxgsj">暂无相关数据</p>
</div>
)
}
}
export default NoneDatas;

@ -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 (
<React.Fragment>
<div className={index===0?"ws100s edu-back-white xiaoshou": "ws100s edu-back-white mt20 xiaoshou"}>
<div className="ws100s teacherentrydiv" onClick={isAdmin?(e)=>{e.stopPropagation();this.props.qiandaoxiangq(true,item.id)}:""}>
<p className={isAdmin?"ws100s teachedivp ymaxnamewidthdivp xiaoshou color-blue":"ws100s teachedivp ymaxnamewidthdivp"} >
{
item.name
}
</p>
<div className="ws100s sortinxdirection mt17">
<div className="teachedivps">
签到人数
</div>
<div className="progressivps">
<Progress percent={jdt} showInfo={false} strokeColor="#1890ff"/>
</div>
<div className="progressivpss">
已签到 {item.normal_count ? item.normal_count : 0} / 应签到 {item.all_count ? item.all_count : 0}
</div>
</div>
<div className="ws100s sortinxdirection mt5">
<div className="ws80s sortinxdirection">
<div className="teachedivps h40s">
签到发起人
</div>
<div className="teachedivpsy ymaxnamewidth60 h40s">
{item.author.user_name}
</div>
<div className="teachedivps ml20 h40s">
签到方式
</div>
<div className="teachedivpsy ymaxnamewidth80 h40s">
{item.mode ?
item.mode === "QRCODE" ?
"二维码签到"
: item.mode === "NUMBER" ?
"数字签到"
: item.mode === "QUICK" ?
"快捷签到"
: ""
: ""}
</div>
<div className="teachedivps ml20 h40s">
开始结束时间
</div>
<div className="teachedivpsy h40s">
{item.attendance_date} {item.start_time}-{item.end_time}
</div>
</div>
<div className="ws20s sortinxdirection">
{
isAdmin === true ?
this.props.defaultActiveKey === "1" ?
<div className="ws100s xaxisreverseorder">
<div className="jiezhis h40s xiaoshou" onClick={(e)=>{e.stopPropagation();this.props.thisEnd(item.id)}}>截止</div>
<div className="shanchu h40s xiaoshou" onClick={(e)=>{e.stopPropagation();this.props.thisdelete(item.id)}}>删除</div>
</div>
:
item.edit_auth === true ?
<div className="ws100s xaxisreverseorder">
<div className="jiezhis h40s xiaoshou" onClick={(e)=>{e.stopPropagation();this.props.Signinnamestypes(item.id,true,item.name)}}>编辑</div>
<div className="shanchu h40s xiaoshou" onClick={(e)=>{e.stopPropagation();this.props.thisdelete(item.id)}}>删除</div>
</div>
:
<div className="ws100s xaxisreverseorder">
<div className="jiezhis h40s color-reds xiaoshou" onClick={(e)=>{e.stopPropagation();this.props.thisdelete(item.id)}}>删除</div>
</div>
:
(
this.props.defaultActiveKey === "1" ?
<div className="ws100s xaxisreverseorder">
{
item.attendance_status?
(
item.attendance_status==="ABSENCE"?
<div className="qiandaobutton xiaoshou" onClick={(e)=>{e.stopPropagation();this.props.Signin(item.mode,item.id,item.attendance_code)}}>
签到
</div>
:
item.attendance_status==="NORMAL"?
<div className="qiandaobutton" >
正常签到
</div>
:""
)
:
""
}
</div>
:
<div className="ws100s xaxisreverseorder">
{
item.attendance_status?
item.attendance_status === "NORMAL" ?
<div className="zcqiandao xiaoshou">
正常签到
</div>
: item.attendance_status === "LEAVE" ?
<div className="qjqiandao xiaoshou">
请假
</div>
: item.attendance_status === "ABSENCE" ?
<div className="kkqiandao xiaoshou">
旷课
</div>
:
""
:
""
}
</div>
)
}
</div>
</div>
</div>
</div>
</React.Fragment>
)
}
}
export default Teacherentry;

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

Loading…
Cancel
Save