Merge branch 'dev_aliyun' into develop

video_transcode
daiao 5 years ago
commit df3adb6542

@ -0,0 +1,81 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-user-schools-statistics-index-page').length > 0) {
var $form = $('.user-schools-statistic-list-form');
// ************** 学校选择 *************
var matcherFunc = function(params, data){
if ($.trim(params.term) === '') {
return data;
}
if (typeof data.text === 'undefined') {
return null;
}
if (data.name && data.name.indexOf(params.term) > -1) {
var modifiedData = $.extend({}, data, true);
return modifiedData;
}
// Return `null` if the term should not be displayed
return null;
}
var defineSchoolSelect = function (schools) {
$form.find('.school-select').select2({
theme: 'bootstrap4',
placeholder: '选择学校/单位',
minimumInputLength: 1,
data: schools,
templateResult: function (item) {
if(!item.id || item.id === '') return item.text;
return item.name;
},
templateSelection: function(item){
if (item.id) {
$form.find('#school_id').val(item.id);
}
return item.name || item.text;
},
matcher: matcherFunc
});
};
// 初始化学校选择器
$.ajax({
url: '/api/schools/for_option.json',
dataType: 'json',
type: 'GET',
success: function(data) {
defineSchoolSelect(data.schools);
}
});
// 清空
$form.on('click', '.clear-btn', function(){
$form.find('select[name="date"]').val('');
$form.find('select[name="province"]').val('');
$form.find('.school-select').val('').trigger('change');
$form.find('input[type="submit"]').trigger('click');
})
// 导出
$('.export-action').on('click', function(){
var form = $(".user-schools-statistic-list-form")
var exportLink = $(this);
var date = form.find("select[name='date']").val();
var schoolId = form.find('input[name="school_id"]').val();
var province = form.find('input[name="province"]').val();
console.log(province)
if(province == "" || province == null){
alert("只能按省份导出");
return;
}
var url = exportLink.data("url").split('?')[0] + "?date=" + date + "&school_id=" + schoolId + "&province=" + province;
window.open(url);
});
}
});

@ -6,12 +6,14 @@ class Admins::DashboardsController < Admins::BaseController
@new_user_count = User.where(created_on: current_month).count
shixun_tomcat = edu_setting('cloud_bridge')
uri = "#{shixun_tomcat}/bridge/monitor/getPodsInfo"
res = interface_get uri, 502, "数据接口延迟"
if res['code'] == 0
@pod_num = res['sum'] || 0
unless Rails.env.development?
shixun_tomcat = edu_setting('cloud_bridge')
uri = "#{shixun_tomcat}/bridge/monitor/getPodsInfo"
res = interface_get uri, 502, "数据接口延迟"
if res['code'] == 0
@pod_num = res['sum'] || 0
end
end
end

@ -0,0 +1,18 @@
class Admins::UserSchoolsStatisticsController < Admins::BaseController
def export
params[:per_page] = 500
_count, @schools = Admins::UserSchoolsStatisticQuery.call(params)
filename = ['用户运营统计', Time.zone.now.strftime('%Y%m%d%H%M%S')].join('-') << '.xlsx'
render xlsx: 'export', filename: filename
end
def index
default_sort('cnt', 'desc')
total_count, schools = Admins::UserSchoolsStatisticQuery.call(params)
@schools = paginate schools, total_count: total_count
end
end

@ -321,7 +321,7 @@ class ApplicationController < ActionController::Base
end
if !User.current.logged? && Rails.env.development?
User.current = User.find 3117
User.current = User.find 1
end

@ -46,7 +46,7 @@ class DiscussesController < ApplicationController
end
sql = "select d.id from discusses d join shixuns s on d.dis_id = s.id where s.status = 2 and s.hidden = false and d.root_id is null
and d.hidden = false #{sql1} #{sql2} order by d.created_at desc"
and d.hidden = false and d.dis_type = 'Shixun' #{sql1} #{sql2} order by d.created_at desc"
memo_ids = Discuss.find_by_sql(sql).pluck(:id)
@memo_count = memo_ids.size
@ -81,8 +81,7 @@ class DiscussesController < ApplicationController
begin
@discuss = Discuss.create!(:dis_id => params[:container_id], :dis_type => params[:container_type],
:content => params[:content].gsub("&nbsp\;", "").strip, :user_id => current_user.id,
:praise_count => 0, :position => params[:position], :challenge_id => params[:challenge_id],
:hidden => !current_user.admin?) # 管理员回复的能够显示
:praise_count => 0, :position => params[:position], :challenge_id => params[:challenge_id])
rescue Exception => e
uid_logger_error("create discuss failed : #{e.message}")
raise Educoder::TipException.new("评论异常,原因:#{e.message}")

@ -418,8 +418,8 @@ class ShixunsController < ApplicationController
logger.info("#########service_update_params: #{service_update_params}")
begin
ActiveRecord::Base.transaction do
@shixun.update_attributes(shixun_params)
@shixun.shixun_info.update_attributes(shixun_info_params)
@shixun.update_attributes!(shixun_params)
@shixun.shixun_info.update_attributes!(shixun_info_params)
# 镜像变动
@shixun.shixun_mirror_repositories.where.not(mirror_repository_id: old_mirror_ids).destroy_all
@shixun.shixun_mirror_repositories.create!(new_mirror_id) if new_mirror_id.present?

@ -1,31 +1,48 @@
class Weapps::AttendancesController < ApplicationController
before_action :require_login
before_action :find_course, only: [:create, :index, :student_attendances]
before_action :find_attendance, except: [:create, :index, :student_attendances]
before_action :find_course, only: [:create, :index, :student_attendances, :history_attendances]
before_action :find_attendance, except: [:create, :index, :student_attendances, :history_attendances]
before_action :user_course_identity
before_action :teacher_allowed, only: [:create]
before_action :edit_auth, only: [:update, :destroy, :end]
def create
ActiveRecord::Base.transaction do
attendance = @course.course_attendances.create!(create_params.merge(user_id: current_user.id))
unless params[:group_ids].blank?
group_ids = @course.charge_group_ids(current_user) & params[:group_ids]
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
CreateStudentAttendanceRecordJob.perform_later(attendance.id, group_ids)
else
@course.course_attendance_groups.create!(course_group_id: 0, course_attendance: attendance)
CreateStudentAttendanceRecordJob.perform_later(attendance.id, [0])
end
render_ok({attendance_id: attendance.id})
end
end
def index
tip_exception(403) if @user_course_identity >= Course::STUDENT
current_date = Date.current
current_end_time = Time.current.strftime("%H:%M:%S")
@current_attendance = @course.course_attendances.where("attendance_date = '#{current_date}' and end_time > '#{current_end_time}'")
.order("attendance_date asc, start_time asc")
all_attendances = @course.course_attendances.where("attendance_date < '#{current_date}' or (attendance_date = '#{current_date}' and end_time < '#{current_end_time}')")
@all_member_attendances = CourseMemberAttendance.where(course_attendance_id: all_attendances)
if params[:group_id].present?
all_attendances = all_attendances.joins(:course_attendance_groups).where(course_attendance_groups: {course_group_id: [params[:group_id], 0]})
@all_member_attendances = @all_member_attendances.joins(:course_member).where(course_members: {course_group_id: params[:group_id]})
end
@history_attendances = all_attendances.order("id asc")
@all_history_count = @history_attendances.size
end
def student_attendances
tip_exception(403, "") if @user_course_identity != Course::STUDENT
# tip_exception("学生身份的签到列表") if @user_course_identity != Course::STUDENT
member = @course.students.find_by(user_id: current_user.id)
current_date = Date.current
current_end_time = Time.current.strftime("%H:%M:%S")
@ -38,19 +55,19 @@ class Weapps::AttendancesController < ApplicationController
@history_attendances = @course.course_attendances.where(id: all_attendance_ids.uniq).
where("attendance_date < '#{current_date}' or (attendance_date = '#{current_date}' and end_time < '#{current_end_time}')").order("id desc")
@current_attendance = @course.course_attendances.where(id: all_attendance_ids.uniq).
where("attendance_date = '#{current_date}' and start_time <= '#{current_end_time}' and end_time > '#{current_end_time}'").take
where("attendance_date = '#{current_date}' and start_time <= '#{current_end_time}' and end_time > '#{current_end_time}'")
@history_count = @history_attendances.size
student_attendance_ids = @history_attendances.pluck(:id)
student_attendance_ids += @current_attendance.present? ? [@current_attendance.id] : []
student_attendance_ids += @current_attendance.present? ? @current_attendance.pluck(:id) : []
if student_attendance_ids.uniq.blank?
@normal_count = 0
@leave_count = 0
@absence_count = 0
else
@normal_count = @course.course_member_attendances.where(course_attendance_id: student_attendance_ids, attendance_status: 1).size
@leave_count = @course.course_member_attendances.where(course_attendance_id: student_attendance_ids, attendance_status: 2).size
@normal_count = @course.course_member_attendances.where(course_attendance_id: student_attendance_ids, attendance_status: "NORMAL").size
@leave_count = @course.course_member_attendances.where(course_attendance_id: student_attendance_ids, attendance_status: "LEAVE").size
@absence_count = student_attendance_ids.uniq.size - @normal_count - @leave_count
end
@ -59,17 +76,49 @@ class Weapps::AttendancesController < ApplicationController
end
def show
@normal_count = @attendance.normal_count
@leave_count = @attendance.leave_count
@absence_count = @attendance.absence_count
@all_count = @attendance.course_member_attendances.size
@_is_current_attendance = @attendance.current_attendance?
if @attendance.course_attendance_groups.first&.course_group_id.to_i == 0
@groups = @course.course_groups
else
@groups = @course.course_groups.where(id: @attendance.course_attendance_groups.pluck(:course_group_id))
end
@groups = @groups.includes(:course_members) if @_is_current_attendance
@all_attendances = @attendance.course_member_attendances
end
def update
tip_exception(403, "") unless @user_course_identity < Course::PROFESSOR || @attendance.user_id == current_user.id
@attendance.update!(name: params[:name])
render_ok
end
def destroy
@attendance.destroy!
render_ok
end
def history_attendances
current_date = Date.current
current_end_time = Time.current.strftime("%H:%M:%S")
@history_attendances = @course.course_attendances.where("attendance_date < '#{current_date}' or
(attendance_date = '#{current_date}' and end_time < '#{current_end_time}')").order("id desc")
@all_history_count = @history_attendances.size
@history_attendances = paginate @history_attendances.includes(:course_member_attendances)
end
def end
a_end_time = "#{@attendance.attendance_date} #{@attendance.end_time}".to_time
tip_exception("该签到已截止") unless @attendance.current_attendance?
@attendance.update!(end_time: Time.current)
render_ok
end
private
@ -81,4 +130,8 @@ class Weapps::AttendancesController < ApplicationController
@attendance = CourseAttendance.find params[:id]
@course = @attendance.course
end
def edit_auth
tip_exception(403, "") unless @user_course_identity < Course::PROFESSOR || @attendance.user_id == current_user.id
end
end

@ -1,9 +1,21 @@
class Weapps::CourseMemberAttendancesController < ApplicationController
before_action :require_login
before_action :find_course, :user_course_identity, only: [:update_status]
def index
attendance = CourseAttendance.find params[:attendance_id]
@member_attendances = attendance.course_member_attendances
if params[:group_ids].present?
@member_attendances = @member_attendances.joins(:course_member).where(course_members: {course_group_id: params[:group_ids]})
end
@member_attendances = @member_attendances.where(attendance_status: params[:attendance_status]) if params[:attendance_status].present?
@member_attendances = @member_attendances.joins(user: :user_extension).order("attendance_status=1 desc, course_member_attendances.updated_at desc, user_extensions.student_id asc")
@member_attendances = paginate @member_attendances.preload(user: :user_extension)
end
def create
tip_exception("签到码不能为空") if params[:code].blank?
tip_exception("attendance_mode参数不对") if [1, 2].include?(params[:attendance_mode])
tip_exception("attendance_mode参数不对") unless ["NUMBER", "QRCODE"].include?(params[:attendance_mode])
attendance = CourseAttendance.find_by(attendance_code: params[:code])
tip_exception("签到码输入有误") if attendance.blank? || attendance.course.blank?
@ -11,19 +23,38 @@ class Weapps::CourseMemberAttendancesController < ApplicationController
member = attendance.course.students.find_by(user_id: current_user.id)
tip_exception("签到码输入有误") if member.blank?
start_time = "#{attendance.attendance_date} #{attendance.start_time}".to_time
end_time = "#{attendance.attendance_date} #{attendance.end_time}".to_time
Rails.logger.info("##############{start_time} #{end_time}")
tip_exception("不在签到时间内") unless start_time < Time.current && Time.current < end_time
tip_exception("不在签到时间内") unless attendance.current_attendance?
current_attendance = attendance.course_member_attendances.find_by(user_id: current_user.id)
tip_exception("请勿重复签到") if current_attendance.present? && current_attendance.attendance_status == 1
tip_exception("您当前是请假状态,无法签到") if current_attendance.present? && current_attendance.attendance_status == 2
tip_exception("您当前是旷课状态,无法签到") if current_attendance.present? && current_attendance.attendance_status == 0
tip_exception("只支持数字签到") if attendance.mode != "ALL" && attendance.mode == "NUMBER" && params[:attendance_mode] == "QRCODE"
tip_exception("只支持二维码签到") if attendance.mode != "ALL" && attendance.mode == "QRCODE" && params[:attendance_mode] == "NUMBER"
unless current_attendance.present?
current_attendance = attendance.course_member_attendances.find_by(user_id: current_user.id)
if current_attendance.present?
tip_exception("请勿重复签到") if current_attendance.attendance_status == "NORMAL"
tip_exception("您当前是请假状态,无法签到") if current_attendance.attendance_status == "LEAVE"
tip_exception("您当前是旷课状态,无法签到") if current_attendance.attendance_status == "ABSENCE" && current_attendance.attendance_mode == "TEACHER"
current_attendance.update!(attendance_status: "NORMAL", attendance_mode: params[:attendance_mode])
else
attendance.course_member_attendances.create!(course_member_id: member.id, user_id: current_user.id, course_id: attendance.course_id,
course_group_id: member.course_group_id, attendance_status: 1, attendance_mode: params[:attendance_mode] || 2)
course_group_id: member.course_group_id, attendance_status: "NORMAL", attendance_mode: params[:attendance_mode])
end
render_ok
end
def update_status
tip_exception("user_id不能为空") if params[:user_id].blank?
tip_exception(403, "无权限调整签到状态") if @user_course_identity > Course::ASSISTANT_PROFESSOR
tip_exception("attendance_status参数不对") unless ["NORMAL", "LEAVE", "ABSENCE"].include?(params[:attendance_status])
attendance = @course.course_attendances.find_by!(id: params[:attendance_id])
current_attendance = attendance.course_member_attendances.find_by(user_id: params[:user_id])
if current_attendance.present?
current_attendance.update!(attendance_status: params[:attendance_status], attendance_mode: "TEACHER")
else
member = attendance.course.students.find_by(user_id: params[:user_id])
tip_exception( "该用户非课堂学生") if member.blank?
attendance.course_member_attendances.create!(course_member_id: member.id, user_id: params[:user_id], course_id: attendance.course_id,
course_group_id: member.course_group_id, attendance_status: params[:attendance_status], attendance_mode: "TEACHER")
end
render_ok
end

@ -8,18 +8,32 @@ class Weapps::CoursesController < Weapps::BaseController
def course_activities
@course = current_course
homework_commons = @course.homework_commons.where(homework_type: ["practice", "normal"]).homework_published
member = @course.course_members.find_by(user_id: current_user.id, is_active: 1)
# 签到数据
attendances = @course.course_attendances
current_date = Date.current
current_end_time = Time.current.strftime("%H:%M:%S")
if @user_course_identity == Course::STUDENT
attendances = attendances.joins(:course_attendance_groups).where(course_attendance_groups: {course_group_id: [member.try(:course_group_id).to_i, 0]})
.where("attendance_date < '#{current_date}' or (attendance_date = '#{current_date}' and start_time < '#{current_end_time}')")
end
attendance_ids = attendances.blank? ? "(-1)" : "(" + attendances.pluck(:id).join(",") + ")"
homework_commons = @course.homework_commons.where(homework_type: ["practice", "normal"]).homework_published
if (@user_course_identity == Course::STUDENT && member.try(:course_group_id).to_i == 0) || @user_course_identity > Course::STUDENT
homework_commons = homework_commons.unified_setting
elsif @user_course_identity == Course::STUDENT
not_homework_ids = @course.homework_group_settings.none_published.where("course_group_id = #{member.try(:course_group_id)}").pluck(:homework_common_id)
not_homework_ids = @course.homework_group_settings.none_published.where("course_group_id = #{member.try(:course_group_id)}")
.pluck(:homework_common_id)
homework_commons = homework_commons.where.not(id: not_homework_ids)
end
homework_ids = homework_commons.blank? ? "(-1)" : "(" + homework_commons.pluck(:id).join(",") + ")"
activities = @course.course_activities.where("course_act_type in ('Course', 'CourseMessage') or
(course_act_type = 'HomeworkCommon' and course_act_id in #{homework_ids})").order("id desc")
(course_act_type = 'HomeworkCommon' and course_act_id in #{homework_ids}) or
(course_act_type = 'CourseAttendance' and course_act_id in #{attendance_ids})").order("id desc")
@activities_count = activities.size
@activities = paginate activities.includes(:course_act, user: :user_extension)
end

@ -2,6 +2,19 @@ module Weapps::AttendancesHelper
def student_attendance_status attendance, user
st_attendance = attendance.course_member_attendances.find_by(user_id: user.id)
st_attendance.present? ? st_attendance.attendance_status : 0
st_attendance.present? ? st_attendance.attendance_status : "ABSENCE"
end
def group_attendance_count attendances, group
course_member_ids = group.course_members.pluck(:id)
attendances.select{|attendance| course_member_ids.include?(attendance.course_member_id) && attendance.attendance_status == "NORMAL"}.size
end
def history_member_count member_attendances, status, attendance_id
member_attendances.select{|member_attendance| member_attendance.attendance_status == status && member_attendance.course_attendance_id == attendance_id}.size
end
def cal_rate base, sum
sum == 0 ? 0 : (base.to_f / sum)
end
end

@ -0,0 +1,26 @@
class CreateStudentAttendanceRecordJob < ApplicationJob
queue_as :default
def perform(attendance_id, group_ids)
attendance = CourseAttendance.find_by(id: attendance_id)
course = attendance.course
return if attendance.blank? || course.blank?
if group_ids.include?(0)
students = course.students
else
students = course.students.where(course_group_id: group_ids)
end
attrs = %i[course_attendance_id user_id course_member_id course_id course_group_id created_at updated_at]
same_attrs = {course_attendance_id: attendance.id, course_id: course.id}
CourseMemberAttendance.bulk_insert(*attrs) do |worker|
students.each do |student|
worker.add same_attrs.merge(user_id: student.user_id, course_member_id: student.id, course_group_id: student.course_group_id)
end
end
end
end

@ -0,0 +1,28 @@
class StudentJoinAttendanceRecordJob < ApplicationJob
queue_as :default
def perform(member_id)
member = CourseMember.find_by(id: member_id)
course = member&.course
return if member.blank? || 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_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)
attrs = %i[course_attendance_id user_id course_member_id course_id course_group_id created_at updated_at]
same_attrs = {course_member_id: member_id, course_id: course.id, user_id: member.user_id, course_group_id: member.course_group_id}
CourseMemberAttendance.bulk_insert(*attrs) do |worker|
current_attendance_ids.each do |attendance_id|
worker.add same_attrs.merge(course_attendance_id: attendance_id)
end
end
end
end

@ -2,10 +2,11 @@ class CourseActivity < ApplicationRecord
belongs_to :course_act, polymorphic: true
belongs_to :course
belongs_to :user
belongs_to :exercise
belongs_to :poll
belongs_to :course_message
belongs_to :homework_common
belongs_to :exercise, optional: true
belongs_to :poll, optional: true
belongs_to :course_message, optional: true
belongs_to :homework_common, optional: true
belongs_to :course_attendance, optional: true
# after_create :add_course_lead

@ -1,18 +1,54 @@
class CourseAttendance < ApplicationRecord
# status: 0: 未开启1已开启2已截止
# mode: 0 两种签到1 二维码签到2 数字签到
enum mode: { ALL: 0, QRCODE: 1, NUMBER: 2 }
belongs_to :course
belongs_to :user
has_many :course_attendance_groups, dependent: :destroy
has_many :course_member_attendances, dependent: :destroy
has_one :course_act, class_name: 'CourseActivity', as: :course_act, dependent: :destroy
validates :name, presence: true
validates :mode, presence: true
validates :attendance_date, presence: true
validates :start_time, presence: true
validates :end_time, presence: true
after_create :generate_attendance_code
after_create :generate_attendance_code, :act_as_course_activity
# 正常签到人数
def normal_count
course_member_attendances.select{|member_attendance| member_attendance.attendance_status == "NORMAL"}.size
end
# 请假人数
def leave_count
course_member_attendances.select{|member_attendance| member_attendance.attendance_status == "LEAVE"}.size
end
# 旷课人数
def absence_count
course_member_attendances.select{|member_attendance| member_attendance.attendance_status == "ABSENCE"}.size
end
# 总人数
def all_count
course_member_attendances.size
end
def current_attendance?
a_start_time = "#{attendance_date} #{start_time}".to_time
a_end_time = "#{attendance_date} #{end_time}".to_time
a_start_time < Time.current && Time.current < a_end_time
end
#课程动态公共表记录
def act_as_course_activity
CourseActivity.create(user_id: user_id, course_id: course_id, course_act: self)
end
# 延迟生成邀请码
def attendance_code

@ -23,6 +23,12 @@ class CourseMember < ApplicationRecord
# after_destroy :delete_works
# after_create :work_operation
after_create :create_attendance_record
def create_attendance_record
StudentJoinAttendanceRecordJob.perform_later(id)
end
def delete_works
if self.role == "STUDENT"
course = self.course

@ -1,6 +1,8 @@
class CourseMemberAttendance < ApplicationRecord
# attendance_mode 1 二维码签到2 数字签到3 老师签到
# attendance_mode 0 初始数据1 二维码签到2 数字签到3 老师签到
enum attendance_mode: { DEFAULT: 0, QRCODE: 1, NUMBER: 2, TEACHER: 3}
# attendance_status 1 正常签到2 请假0 旷课
enum attendance_status: { NORMAL: 1, LEAVE: 2, ABSENCE: 0 }
belongs_to :course_member
belongs_to :user
belongs_to :course

@ -5,7 +5,7 @@ class ShixunInfo < ApplicationRecord
# validates_presence_of :evaluate_script, message: "实训脚本不能为空"
after_commit :create_diff_record
validates :description, length: { maximum: 5000, too_long: "不能超过5000个字符" }
validates :description, length: { maximum: 10000, too_long: "不能超过10000个字符" }
private

@ -0,0 +1,114 @@
class Admins::UserSchoolsStatisticQuery < ApplicationQuery
include CustomSortable
attr_reader :params
sort_columns :cnt,
default_by: :cnt, default_direction: :desc
def initialize(params)
@params = params
end
def call
schools = School
if params[:province].present?
schools = schools.where("province like ?", "%#{params[:province]}%")
end
if params[:school_id].present?
schools = schools.where(id: params[:school_id])
end
total = schools.count
# 根据排序字段进行查询
schools = query_by_sort_column(schools.group(:id), params[:sort_by])
#schools = custom_sort(schools, params[:sort_by], params[:sort_direction])
schools = schools.limit(page_size).offset(offset).to_a
# 查询并组装其它数据
schools = package_other_data(schools)
[total, schools]
end
private
def package_other_data(schools)
ids = schools.map(&:id)
user_e = UserExtension.where(school_id: schools.map(&:id))
#study_myshixun = Myshixun.joins("join user_extensions ue on ue.user_id = myshixuns.user_id").where(ue: {school_id: ids})
#finish_myshixun = Myshixun.joins("join user_extensions ue on ue.user_id = myshixuns.user_id")
# .where(ue: {school_id: ids}, myshixuns: {status: 1})
study_challenge = Game.joins("join user_extensions ue on ue.user_id = games.user_id")
.where(ue: {school_id: ids},).where( games:{status: [0, 1, 2]})
finish_challenge = Game.joins("join user_extensions ue on ue.user_id = games.user_id")
.where(ue: {school_id: ids}).where(games: {status: 2})
reg_teacher = user_e.where(identity: 'teacher')
reg_student = user_e.where.not(identity: 'teacher')
if time_range.present?
#study_myshixun = study_myshixun.where(updated_at: time_range)
#finish_myshixun = finish_myshixun.where(updated_at: time_range)
study_challenge = study_challenge.where(updated_at: time_range)
finish_challenge = finish_challenge.where(updated_at: time_range)
reg_teacher = reg_teacher.where(created_at: time_range)
reg_student = reg_student.where(created_at: time_range)
user_e = user_e.joins(:user).where(users: {last_login_on: time_range})
end
#study_myshixun_map = study_myshixun.reorder(nil).group(:school_id).count
#finish_myshixun_map = finish_myshixun.reorder(nil).group(:school_id).count
study_challenge_map = study_challenge.reorder(nil).group(:school_id).count
finish_challenge_map = finish_challenge.reorder(nil).group(:school_id).count
evaluate_count_map = study_challenge.reorder(nil).group(:school_id).sum(:evaluate_count)
reg_teacher_map = reg_teacher.reorder(nil).group(:school_id).count
reg_student_map = reg_student.reorder(nil).group(:school_id).count
user_e_map = user_e.reorder(nil).group(:school_id).count
schools.each do |school|
school._extra_data = {
#study_shixun_count: study_myshixun_map.fetch(schools.id, 0),
#finish_shixun_count: finish_myshixun_map.fetch(schools.id, 0),
study_challenge_count: study_challenge_map.fetch(school.id, 0),
finish_challenge_count: finish_challenge_map.fetch(school.id, 0),
evaluate_count: evaluate_count_map.fetch(school.id, 0),
reg_teacher_count: reg_teacher_map.fetch(school.id, 0),
reg_student_count: reg_student_map.fetch(school.id, 0),
user_active_count: user_e_map.fetch(school.id, 0)
}
end
schools
end
def query_by_sort_column(schools, sort_by_column)
#base_query_column = 'schools.*'
case sort_by_column.to_s
when 'cnt' then
schools.left_joins(:user_extensions).select("schools.*, count(*) cnt").order("cnt desc")
else
schools
end
end
def time_range
@_time_range ||= begin
case params[:date]
when 'dayly' then 1.days.ago..Time.now
when 'weekly' then 1.weeks.ago..Time.now
when 'monthly' then 1.months.ago..Time.now
when 'quarterly' then 3.months.ago..Time.now
when 'yearly' then 1.years.ago..Time.now
else ''
end
end
end
def page_size
params[:per_page].to_i.zero? ? 20 : params[:per_page].to_i
end
def offset
(params[:page].to_i.zero? ? 0 : params[:page].to_i - 1) * page_size
end
end

@ -9,6 +9,10 @@ class ApplicationService
content.gsub(regex, '')
end
def convert_https content
content.gsub("http:", "https:")
end
private
def strip(str)

@ -12,7 +12,7 @@ class Videos::DispatchCallbackService < ApplicationService
# TODO:: 拆分事件分发
case params['EventType']
when 'FileUploadComplete' then # 视频上传完成
video.file_url = params['FileUrl']
video.file_url = convert_https(params['FileUrl'])
video.filesize = params['Size']
video.upload_success
video.save!

@ -62,6 +62,7 @@
<%= sidebar_item_group('#user-submenu', '用户', icon: 'user') do %>
<li><%= sidebar_item(admins_users_path, '用户列表', icon: 'user', controller: 'admins-users') %></li>
<li><%= sidebar_item(admins_user_statistics_path, '用户实训情况', icon: 'area-chart', controller: 'admins-user_statistics') %></li>
<li><%= sidebar_item(admins_user_schools_statistics_path, '用户运营统计', icon: 'user-md', controller: 'admins-user_schools_statistics') %></li>
<% end %>
</li>

@ -0,0 +1,18 @@
wb = xlsx_package.workbook
wb.add_worksheet(name: '用户运营统计') do |sheet|
sheet.add_row %w(单位名称 省份 注册老师数量 注册学生数量 活跃用户 学习关卡数 完成关卡数 评测次数)
@schools.each do |school|
data = [
school.name,
school.province,
school.display_extra_data(:reg_teacher_count),
school.display_extra_data(:reg_student_count),
school.display_extra_data(:user_active_count),
school.display_extra_data(:study_challenge_count),
school.display_extra_data(:finish_challenge_count),
school.display_extra_data(:evaluate_count),
]
sheet.add_row(data)
end
end

@ -0,0 +1,30 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('用户运营情况') %>
<% end %>
<div class="box search-form-container user-schools-statistic-list-form">
<%= form_tag(admins_user_schools_statistics_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %>
<div class="form-group col-12 col-md-auto">
<label for="status">时间范围:</label>
<% data_arrs = [['不限', ''],['最近一天', 'dayly'], ['最近一周', 'weekly'], ['最近一个月', 'monthly'],
['最近三个月', 'quarterly'], ['最近一年', 'yearly']] %>
<%= select_tag(:date, options_for_select(data_arrs, params[:date]), class: 'form-control') %>
</div>
<div class="form-group col-12 col-md-3">
<label for="school_name">所属单位:</label>
<%= hidden_field_tag(:school_id, params[:school_id]) %>
<%= select_tag :school_name, options_for_select([''], params[:school_id]), class: 'form-control school-select flex-1' %>
</div>
<%= text_field_tag(:province, params[:province], class: 'form-control col-sm-2 ml-3', placeholder: '所属省份') %>
<%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
<input type="reset" class="btn btn-secondary clear-btn" value="清空"/>
<% end %>
<%= javascript_void_link '导出', class: 'btn btn-outline-primary export-action', 'data-url': export_admins_user_schools_statistics_path(format: :xlsx) %>
</div>
<div class="box admin-list-container user-schools-statistic-list-container">
<%= render partial: 'admins/user_schools_statistics/shared/list', locals: { schools: @schools } %>
</div>

@ -0,0 +1 @@
$('.user-schools-statistic-list-container').html("<%= j( render partial: 'admins/user_schools_statistics/shared/list', locals: { schools: @schools } ) %>");

@ -0,0 +1,38 @@
<table class="table table-hover text-center user-statistic-list-table">
<thead class="thead-light">
<tr>
<th width="6%">序号</th>
<th width="22%" class="text-left">单位名称</th>
<th width="8%" class="text-left">省份</th>
<th width="10%">注册老师数量<%#= sort_tag('学习关卡数', name: 'study_challenge_count', path: admins_user_statistics_path) %></th>
<th width="10%">注册学生数量<%#= sort_tag('完成关卡数', name: 'finish_challenge_count', path: admins_user_statistics_path) %></th>
<th width="10%">活跃用户<%#= sort_tag('活跃用户', name: 'user_active_count', path: admins_user_schools_statistics_path) %></th>
<th width="10%">学习关卡数<%#= sort_tag('学习关卡数', name: 'finish_shixun_count', path: admins_user_schools_statistics_path) %></th>
<th width="10%">完成关卡数</th>
<th width="14%">评测次数</th>
</tr>
</thead>
<tbody>
<% if schools.present? %>
<% schools.each_with_index do |school, index| %>
<tr class="user-statistic-item-<%= school.id %>">
<td><%= list_index_no((params[:page] || 1).to_i, index) %></td>
<td class="text-left">
<%= school.name %>
</td>
<td class="text-left"><%= school.province %></td>
<td><%= school.display_extra_data(:reg_teacher_count) %></td>
<td><%= school.display_extra_data(:reg_student_count) %></td>
<td><%= school.display_extra_data(:user_active_count) %></td>
<td><%= school.display_extra_data(:study_challenge_count) %></td>
<td><%= school.display_extra_data(:finish_challenge_count) %></td>
<td><%= school.display_extra_data(:evaluate_count) %></td>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: schools } %>

@ -0,0 +1,9 @@
json.history_attendances @history_attendances do |attendance|
json.(attendance, :id, :name)
json.created_at attendance.created_at.strftime("%Y/%m/%d %H:%M")
json.normal_count attendance.normal_count
json.leave_count attendance.leave_count
json.absence_count attendance.absence_count
json.edit_auth @user_course_identity < Course::PROFESSOR || attendance.user_id == User.current.id
end
json.all_history_count @all_history_count

@ -0,0 +1,24 @@
json.current_attendance @current_attendance do |attendance|
json.(attendance, :id, :normal_count, :all_count)
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")
end
all_normal_rate = []
all_absence_rate = []
json.history_attendances @history_attendances.each_with_index.to_a do |attendance, index|
normal_count = history_member_count(@all_member_attendances, "NORMAL", attendance.id)
absence_count = history_member_count(@all_member_attendances, "ABSENCE", attendance.id)
all_count = @all_member_attendances.select{|member_attendance| member_attendance.course_attendance_id == attendance.id}.size
json.index index + 1
json.normal_rate cal_rate(normal_count, all_count)
all_normal_rate << cal_rate(normal_count, all_count)
json.absence_rate cal_rate(absence_count, all_count)
all_absence_rate << cal_rate(absence_count, all_count)
end
json.all_history_count @all_history_count
json.avg_normal_rate @all_history_count == 0 ? 0 : all_normal_rate.sum / @all_history_count
json.avg_absence_rate @all_history_count == 0 ? 0 : all_absence_rate.sum / @all_history_count

@ -0,0 +1,15 @@
json.normal_count @normal_count
json.leave_count @leave_count
json.absence_count @absence_count
json.all_count @all_count
json.code @attendance.attendance_code
json.mode @attendance.mode
json.edit_auth @user_course_identity < Course::PROFESSOR || @attendance.user_id == User.current.id
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.course_groups @groups do |group|
json.(group, :id, :name, :course_members_count)
json.attendance_count group_attendance_count(@all_attendances, group) if @_is_current_attendance
end

@ -1,5 +1,5 @@
json.current_attendance do
json.partial! 'student_attendance', locals: {attendance: @current_attendance} if @current_attendance.present?
json.current_attendance @current_attendance do |attendance|
json.partial! 'student_attendance', locals: {attendance: attendance}
end
json.history_attendances @history_attendances do |attendance|

@ -0,0 +1,5 @@
json.member_attendances @member_attendances.each do |member|
json.(member, :user_id, :attendance_status)
json.user_name member.user&.real_name
json.student_id member.user&.student_id
end

@ -870,6 +870,7 @@ Rails.application.routes.draw do
collection do
get :school_list
get :for_option
get :for_province_option
get :search
end
@ -1061,11 +1062,15 @@ Rails.application.routes.draw do
resources :attendances, only: [:index, :update, :create, :show, :destroy], shallow: true do
collection do
get :student_attendances
get :history_attendances
end
post :end, on: :member
end
end
resources :course_member_attendances, only: [:create]
resources :course_member_attendances, only: [:create, :index] do
post :update_status, on: :collection
end
resources :homework_commons do
post :update_settings, on: :member
@ -1159,6 +1164,9 @@ Rails.application.routes.draw do
resources :user_statistics, only: [:index] do
get :export, on: :collection
end
resources :user_schools_statistics, only: [:index] do
get :export, on: :collection
end
resources :library_applies, only: [:index] do
member do
post :agree

@ -0,0 +1,5 @@
class ModidyHiddenDefaultForDiscusses < ActiveRecord::Migration[5.2]
def change
change_column :discusses, :hidden, :boolean, :default => false
end
end

@ -5,7 +5,7 @@ namespace :sync_evaluate do
School.find_each do |school|
puts school.id
evaluate_count = Game.find_by_sql("select sum(g.evaluate_count) as e_count from games g, user_extensions ue where
g.user_id = ue.user_id and ue.school_id = #{school}.id").firt.try(:e_count)
g.user_id = ue.user_id and ue.school_id = '#{school.id}'").first.try(:e_count)
report = SchoolReport.find_or_initialize_by(school_id: school.id)
report.school_name = school.name

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -140264,6 +140264,87 @@ $(document).on('turbolinks:load', function() {
});
}
});
$(document).on('turbolinks:load', function() {
if ($('body.admins-user-schools-statistics-index-page').length > 0) {
var $form = $('.user-schools-statistic-list-form');
// ************** 学校选择 *************
var matcherFunc = function(params, data){
if ($.trim(params.term) === '') {
return data;
}
if (typeof data.text === 'undefined') {
return null;
}
if (data.name && data.name.indexOf(params.term) > -1) {
var modifiedData = $.extend({}, data, true);
return modifiedData;
}
// Return `null` if the term should not be displayed
return null;
}
var defineSchoolSelect = function (schools) {
$form.find('.school-select').select2({
theme: 'bootstrap4',
placeholder: '选择学校/单位',
minimumInputLength: 1,
data: schools,
templateResult: function (item) {
if(!item.id || item.id === '') return item.text;
return item.name;
},
templateSelection: function(item){
if (item.id) {
$form.find('#school_id').val(item.id);
}
return item.name || item.text;
},
matcher: matcherFunc
});
};
// 初始化学校选择器
$.ajax({
url: '/api/schools/for_option.json',
dataType: 'json',
type: 'GET',
success: function(data) {
defineSchoolSelect(data.schools);
}
});
// 清空
$form.on('click', '.clear-btn', function(){
$form.find('select[name="date"]').val('');
$form.find('select[name="province"]').val('');
$form.find('.school-select').val('').trigger('change');
$form.find('input[type="submit"]').trigger('click');
})
// 导出
$('.export-action').on('click', function(){
var form = $(".user-schools-statistic-list-form")
var exportLink = $(this);
var date = form.find("select[name='date']").val();
var schoolId = form.find('input[name="school_id"]').val();
var province = form.find('input[name="province"]').val();
console.log(province)
if(province == "" || province == null){
alert("只能按省份导出");
return;
}
var url = exportLink.data("url").split('?')[0] + "?date=" + date + "&school_id=" + schoolId + "&province=" + province;
window.open(url);
});
}
});
$(document).on('turbolinks:load', function() {
if ($('body.admins-user-statistics-index-page').length > 0) {
var $form = $('.user-statistic-list-form');

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -793,37 +793,37 @@ class App extends Component {
render={
(props) => (<Paperreview {...this.props} {...props} {...this.state} />)
}/>
<Route path="/paperlibrary/edit/:id"
render={
(props) => (<Paperlibraryeditid {...this.props} {...props} {...this.state} />)
}/>
{/*<Route path="/paperlibrary/edit/:id"*/}
{/* render={*/}
{/* (props) => (<Paperlibraryeditid {...this.props} {...props} {...this.state} />)*/}
{/* }/>*/}
<Route path="/paperlibrary/see/:id"
render={
(props) => (<Paperlibraryseeid {...this.props} {...props} {...this.state} />)
}/>
{/*<Route path="/paperlibrary/see/:id"*/}
{/* render={*/}
{/* (props) => (<Paperlibraryseeid {...this.props} {...props} {...this.state} />)*/}
{/* }/>*/}
<Route path="/myproblems/:id/:tab?"
render={
(props) => (<StudentStudy {...this.props} {...props} {...this.state} />)
} />
<Route path="/question/edit/:id"
render={
(props) => (<Questionitem_banks {...this.props} {...props} {...this.state} />)
} />
<Route path="/question/newitem"
render={
(props) => (<Questionitem_banks {...this.props} {...props} {...this.state} />)
} />
<Route path="/question/:type"
render={
(props) => (<Headplugselection {...this.props} {...props} {...this.state} />)
} />
<Route path="/paperlibrary"
render={
(props) => (<Testpaperlibrary {...this.props} {...props} {...this.state} />)
}/>
{/*<Route path="/question/edit/:id"*/}
{/* render={*/}
{/* (props) => (<Questionitem_banks {...this.props} {...props} {...this.state} />)*/}
{/* } />*/}
{/*<Route path="/question/newitem"*/}
{/* render={*/}
{/* (props) => (<Questionitem_banks {...this.props} {...props} {...this.state} />)*/}
{/* } />*/}
{/*<Route path="/question/:type"*/}
{/* render={*/}
{/* (props) => (<Headplugselection {...this.props} {...props} {...this.state} />)*/}
{/* } />*/}
{/*<Route path="/paperlibrary"*/}
{/* render={*/}
{/* (props) => (<Testpaperlibrary {...this.props} {...props} {...this.state} />)*/}
{/* }/>*/}
<Route path="/Integeneration"
render={
@ -835,15 +835,19 @@ class App extends Component {
(props) => (<Developer {...this.props} {...props} {...this.state} />)
}/>
<Route path="/question"
render={
(props) => (<Headplugselection {...this.props} {...props} {...this.state} />)
}/>
{/*<Route path="/question"*/}
{/* render={*/}
{/* (props) => (<Headplugselection {...this.props} {...props} {...this.state} />)*/}
{/* }/>*/}
{/*<Route path="/wxcode/:identifier?" component={WXCode}*/}
{/* render={*/}
{/* (props)=>(<WXCode {...this.props} {...props} {...this.state}></WXCode>)*/}
{/* }*/}
{/*/>*/}
<Route exact path="/"
// component={ShixunsHome}
render={

@ -9,7 +9,7 @@ import axios from 'axios';
const { TextArea } = Input;
const { Option } = Select;
const array=['腾讯课堂','B站','斗鱼','威佰通'];
const array=['腾讯课堂','斗鱼直播','Bilibili','威佰通'];
function range(start, end) {
const result = [];
@ -212,6 +212,19 @@ class LiveNew extends Component{
className="liveModal"
>
<Spin spinning={isSpining}>
<style>
{`
.ant-select-dropdown-menu-item{
text-align:center;
padding: 10px 0px;
border-bottom: 1px solid #eee;
}
.ant-select-dropdown-menu-item:last-child{
border-bottom:none;
}
`
}
</style>
<div className="task-popup-content">
<Form onSubmit={this.handleSubmit}>
<Form.Item label={`直播课程`}>
@ -229,6 +242,7 @@ class LiveNew extends Component{
placeholder="请选择或输入直播平台名称"
onChange={this.ChangePlatform}
dataSource={dataSource}
className="plateAutoComplete"
>
</AutoComplete>
)}

@ -1298,10 +1298,12 @@ class CommonWorkSetting extends Component{
{/* 匿评数量 */}
<div className={"h20 mb30 ml60"}>
<span>匿评数量</span>
<Tooltip placement="bottom" title={starttimetype===true?this.props.isAdmin()?"发布时间已过,则不能修改":"":""}>
<Tooltip placement="bottom" title={this.props.isAdmin()?moment(init_evaluation_start) < this.fetchMoment?"匿评已开启无法修改匿评数量":"":""}>
<span>
<Input type="number" className="mr10" style={{width:"100px" }} value={evaluation_num} onInput={this.evaluation_num_change}
disabled={anonymous_comment && !noAuth? false : true} min={0} max={100}
disabled={anonymous_comment && !noAuth?
moment(init_evaluation_start) < this.fetchMoment?true:false
: true} min={0} max={100}
/>
</span>
</Tooltip>

@ -525,7 +525,6 @@ class Coursesleftnav extends Component{
}
saveNavmodapost=(url,value,positiontype,coursesId)=>{
axios.post(url,
{name:value}).then((result)=>{
if(result!=undefined){
@ -554,7 +553,8 @@ class Coursesleftnav extends Component{
}
if(positiontype==="course_groups"){
window.location.href=`/courses/${coursesId}/course_groups/${result.data.group_id}`;
this.props.updataleftNavfun();
this.props.history.push(`/courses/${coursesId}/course_groups/${result.data.group_id}`);
}
}
@ -585,6 +585,7 @@ class Coursesleftnav extends Component{
}
saveNavmoda=()=>{
debugger;
let {Navmodaltypename,setnavid,NavmodalValue}=this.state;
let id =setnavid;

@ -66,7 +66,7 @@ function CourseGroupListTable(props) {
className:"color-grey-6",
render: (name, record, index) => {
return <WordsBtn title={name.length > 11 ? name : ''} onClick={() => onGoDetail(record)} style={''}
className="overflowHidden1" style2={{maxWidth: '180px', verticalAlign: 'bottom'}}>
className="overflowHidden1 color-dark" style2={{maxWidth: '180px', verticalAlign: 'bottom'}}>
{name}</WordsBtn>
}
},
@ -154,7 +154,7 @@ function CourseGroupListTable(props) {
{!isCourseEnd && isAdmin && <WordsBtn style2={{ marginRight: '12px' }} onClick={() => onDelete(record)} style={'grey'}>删除分班</WordsBtn>}
{isStudent && <WordsBtn style2={{ marginRight: '12px' }} onClick={() => addToDir(record)} style={''}>加入分班</WordsBtn>}
<WordsBtn onClick={() => onGoDetail(record)} style={''}>查看</WordsBtn>
<WordsBtn onClick={() => onGoDetail(record)} style={''} className="color-dark">查看</WordsBtn>
</React.Fragment>
}
})

@ -87,7 +87,9 @@ class Shixuninformation extends Component {
this.props.form.setFieldsValue({
selectscripts: this.props.data && this.props.data.shixun.standard_scripts[0].id
})
this.get_mirror_script(this.props.data && this.props.data.shixun.standard_scripts[0].id)
console.log(this.props);
// debugger;
// this.get_mirror_script(this.props.data && this.props.data.shixun.standard_scripts[0].id)
} else {
this.props.form.setFieldsValue({
selectscripts: this.props.data && this.props.data.shixun.choice_standard_scripts

@ -1,48 +1,30 @@
import React, {Component} from 'react';
import React, { Component } from 'react';
import {Input, Select, Radio, Checkbox, Popconfirm, message, Modal} from 'antd';
import {BrowserRouter as Router, Route, Link, Switch} from "react-router-dom";
// import "antd/dist/antd.css";
import { getImageUrl, getUploadActionUrl, getUrl } from 'educoder';
import { getUploadActionUrl, getUrl } from 'educoder';
import '../../courses/css/Courses.css'
import axios from 'axios';
import './css/TPMchallengesnew.css';
require('codemirror/lib/codemirror.css');
let origin = getUrl();
let path = '/editormd/lib/'
path = getUrl("/editormd/lib/")
path = getUrl("/editormd/lib/")
const $ = window.$;
let timeout;
let currentValue;
const Option = Select.Option;
const RadioGroup = Radio.Group;
// 保存数据
function md_add_data(k,mdu,d){
window.sessionStorage.setItem(k+mdu,d);
function md_add_data(k, mdu, d) {
window.sessionStorage.setItem(k + mdu, d);
}
// 清空保存的数据
function md_clear_data(k,mdu,id){
window.sessionStorage.removeItem(k+mdu);
var id1 = "#e_tip_"+id;
var id2 = "#e_tips_"+id;
if(k == 'content'){
function md_clear_data(k, mdu, id) {
window.sessionStorage.removeItem(k + mdu);
var id1 = "#e_tip_" + id;
var id2 = "#e_tips_" + id;
if (k == 'content') {
$(id2).html(" ");
}else{
} else {
$(id1).html(" ");
}
}
@ -59,16 +41,16 @@ function md_rec_data(k, mdu, id) {
}
window.md_rec_data = md_rec_data;
function md_elocalStorage(editor,mdu,id){
if (window.sessionStorage){
var oc = window.sessionStorage.getItem('content'+mdu);
if(oc !== null && oc != editor.getValue()){
console.log("#e_tips_"+id)
$("#e_tips_"+id).data('editor', editor);
var h = '您上次有已保存的数据,是否<a style="cursor: pointer;" class="link-color-blue" onclick="md_rec_data(\'content\',\''+ mdu + '\',\'' + id + '\')">恢复</a> ? / <a style="cursor: pointer;" class="link-color-blue" onclick="md_clear_data(\'content\',\''+ mdu + '\',\'' + id + '\')">不恢复</a>';
$("#e_tips_"+id).html(h);
function md_elocalStorage(editor, mdu, id) {
if (window.sessionStorage) {
var oc = window.sessionStorage.getItem('content' + mdu);
if (oc !== null && oc != editor.getValue()) {
console.log("#e_tips_" + id)
$("#e_tips_" + id).data('editor', editor);
var h = '您上次有已保存的数据,是否<a style="cursor: pointer;" class="link-color-blue" onclick="md_rec_data(\'content\',\'' + mdu + '\',\'' + id + '\')">恢复</a> ? / <a style="cursor: pointer;" class="link-color-blue" onclick="md_clear_data(\'content\',\'' + mdu + '\',\'' + id + '\')">不恢复</a>';
$("#e_tips_" + id).html(h);
}
setInterval(function() {
setInterval(function () {
var d = new Date();
var h = d.getHours();
var m = d.getMinutes();
@ -76,46 +58,46 @@ function md_elocalStorage(editor,mdu,id){
h = h < 10 ? '0' + h : h;
m = m < 10 ? '0' + m : m;
s = s < 10 ? '0' + s : s;
if(editor.getValue().trim() != ""){
md_add_data("content",mdu,editor.getValue());
var id1 = "#e_tip_"+id;
var id2 = "#e_tips_"+id;
if (editor.getValue().trim() != "") {
md_add_data("content", mdu, editor.getValue());
var id1 = "#e_tip_" + id;
var id2 = "#e_tips_" + id;
var textStart = " 数据已于 "
var text = textStart + h + ':' + m + ':' + s +" 保存 ";
var text = textStart + h + ':' + m + ':' + s + " 保存 ";
// 占位符
var oldHtml = $(id2).html();
if (oldHtml && oldHtml != ' ' && oldHtml.startsWith(textStart) == false) {
$(id2).html( oldHtml.split(' (')[0] + ` (${text})`);
$(id2).html(oldHtml.split(' (')[0] + ` (${text})`);
} else {
$(id2).html(text);
}
// $(id2).html("");
}
},10000);
}, 10000);
}else{
$("#e_tip_"+id).after('您的浏览器不支持localStorage.无法开启自动保存草稿服务,请升级浏览器!');
} else {
$("#e_tip_" + id).after('您的浏览器不支持localStorage.无法开启自动保存草稿服务,请升级浏览器!');
}
}
function create_editorMD(id, width, high, placeholder, imageUrl, callback, initValue,
onchange, watch, { noStorage, showNullButton, emoji }, that) {
function create_editorMD(id, width, high, placeholder, imageUrl, callback, initValue,
onchange, watch, { noStorage, showNullButton, emoji }, that) {
// 还是出现了setting只有一份被共用的问题
var editorName = window.editormd(id, {
width: width,
height: high===undefined?400:high,
path: path, // "/editormd/lib/"
markdown : initValue,
height: high === undefined ? 400 : high,
path: getUrl("/editormd/lib/"), // "/editormd/lib/"
markdown: initValue,
dialogLockScreen: false,
watch:watch===undefined?true:watch,
watch: watch === undefined ? true : watch,
syncScrolling: "single",
tex: true,
tocm: true,
emoji: !!emoji ,
emoji: !!emoji,
taskList: true,
codeFold: true,
searchReplace: true,
@ -123,21 +105,35 @@ function create_editorMD(id, width, high, placeholder, imageUrl, callback, initV
sequenceDiagram: true,
autoFocus: false,
// mine
toolbarIcons: function (mdEditor) {
let react_id = `react_${mdEditor.id}`;
const __that = window[react_id]
// Or return editormd.toolbarModes[name]; // full, simple, mini
// Using "||" set icons align right.
const icons = ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "link", "|", "testIcon", "testIcon1", '|', "image", "table", '|', "watch", "clear"];
const icons = ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "link", "|", "testIcon", "testIcon1", '|', "image", "table", '|', "line-break", "watch", "clear"];
// 试卷处用到的填空题新增按钮
if (__that.props.showNullButton) {
icons.push('nullBtton')
}
return icons
},
toolbarIconsClass: {
"line-break": "fa-minus"
},
toolbarHandlers: {
/**
* @param {Object} cm CodeMirror对象
* @param {Object} icon 图标按钮jQuery元素对象
* @param {Object} cursor CodeMirror的光标对象可获取光标所在行和位置
* @param {String} selection 编辑器选中的文本
*/
"line-break": function (cm, icon, cursor, selection) {
// 如果当前没有选中的文本,将光标移到要输入的位置
if (selection === "") {
cm.setCursor(cursor.line, cursor.ch + 1)
}
cm.replaceSelection("<br/>");
}
},
toolbarCustomIcons: {
testIcon: "<a type=\"inline\" class=\"latex\" ><div class='zbg'></div></a>",
testIcon1: "<a type=\"latex\" class=\"latex\" ><div class='zbg_latex'></div></a>",
@ -152,14 +148,11 @@ function create_editorMD(id, width, high, placeholder, imageUrl, callback, initV
imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp", "JPG", "JPEG", "GIF", "PNG", "BMP", "WEBP"],
imageUploadURL: imageUrl,//url
onchange: onchange,
onload: function() {
onload: function () {
let _id = this.id // 如果要使用this这里不能使用箭头函数
let _editorName = this;
let react_id = `react_${_editorName.id}`;
const __that = window[react_id]
// this.previewing();
// let _id = id;
$("#" + _id + " [type=\"latex\"]").bind("click", function () {
_editorName.cm.replaceSelection("```latex");
_editorName.cm.replaceSelection("\n");
@ -180,19 +173,12 @@ function create_editorMD(id, width, high, placeholder, imageUrl, callback, initV
if (__that.props.showNullButton) {
const NULL_CH = '▁'
// const NULL_CH = ''
// const NULL_CH = '🈳'
$("#" + _id + " [type=\"nullBtton\"]").bind("click", function () {
_editorName.cm.replaceSelection(NULL_CH);
// var __Cursor = _editorName.cm.getDoc().getCursor();
// _editorName.cm.setCursor(__Cursor.line - 1, 0);
});
}
if (noStorage == true) {
} else {
if (!noStorage == true) {
md_elocalStorage(_editorName, `MDEditor__${_id}`, _id);
}
@ -216,11 +202,11 @@ export default class TPMMDEditor extends Component {
// this.answers_editormd.setValue(this.props.initValue)
// }
}
// react_mdEditor_
componentDidMount = () => {
const { mdID, initValue, placeholder, showNullButton} = this.props;
const { mdID, initValue, placeholder, showNullButton } = this.props;
let _id = `mdEditor_${mdID}`
this.contentChanged = false;
const _placeholder = placeholder || "";
@ -235,7 +221,7 @@ export default class TPMMDEditor extends Component {
const __editorName = _editorName;
react_id = `react_${__editorName.id}`;
const that = window[react_id]
// 一个延迟的recreate或resize不加这段代码md初始化可能会出现样式问题
setTimeout(() => {
if (that.props.needRecreate == true) {
@ -256,33 +242,33 @@ export default class TPMMDEditor extends Component {
__editorName.setValue(that.state.initValue)
}
}
__editorName.cm.on("change", (_cm, changeObj) => {
that.contentChanged = true;
if (that.state.showError) {
that.setState({showError: false})
that.setState({ showError: false })
}
that.onEditorChange()
})
that.props.onCMBlur && __editorName.cm.on('blur', () => {
that.props.onCMBlur()
})
that.props.onCMBeforeChange && __editorName.cm.on('beforeChange', (cm,change) => {
that.props.onCMBeforeChange(cm,change)
that.props.onCMBeforeChange && __editorName.cm.on('beforeChange', (cm, change) => {
that.props.onCMBeforeChange(cm, change)
})
that.answers_editormd = __editorName;
// 这里应该可以去掉了,方便调试加的
window[__editorName.id+'_'] = __editorName;
}, initValue, this.onEditorChange,this.props.watch, {
window[__editorName.id + '_'] = __editorName;
}, initValue, this.onEditorChange, this.props.watch, {
noStorage: this.props.noStorage,
showNullButton: this.props.showNullButton,
emoji: this.props.emoji
}, this);
}
// 用在form里时validate失败时出现一个红色边框
showError = () => {
this.setState({showError: true})
this.setState({ showError: true })
}
onEditorChange = () => {
if (!this.answers_editormd) return;
@ -290,7 +276,7 @@ export default class TPMMDEditor extends Component {
//console.log('onEditorChange', this.props.id, val)
try {
this.props.onChange && this.props.onChange(val)
} catch(e) {
} catch (e) {
// http://localhost:3007/courses/1309/common_homeworks/6566/setting
// 从这个页面,跳转到编辑页面,再在编辑页面点击返回的时候,这里会报错
console.error('出错')
@ -322,7 +308,7 @@ export default class TPMMDEditor extends Component {
this.setState({ initValue: val })
}
}
render() {
let {
@ -335,10 +321,10 @@ export default class TPMMDEditor extends Component {
}
return (
<React.Fragment>
<div className={`df ${className} ${imageExpand && 'editormd-image-click-expand' }`} >
{/* padding10-20 */}
<div className="edu-back-greyf5 radius4" id={`mdEditor_${mdID}`} style={{..._style}}>
<textarea style={{display: 'none'}} id={`mdEditors_${mdID}`} name="content"></textarea>
<div className={`df ${className} ${imageExpand && 'editormd-image-click-expand'}`} >
{/* padding10-20 */}
<div className="edu-back-greyf5 radius4" id={`mdEditor_${mdID}`} style={{ ..._style }}>
<textarea style={{ display: 'none' }} id={`mdEditors_${mdID}`} name="content"></textarea>
<div className="CodeMirror cm-s-defualt">
</div>
</div>

@ -8,7 +8,7 @@ import 'antd/lib/select/style/index.css';
import 'antd/lib/input/style/index.css';
import './shixunCss/ShixunSearchBar.css';
import './shixunCss/ShixunSearchBar.css';
import axios from 'axios';
@ -206,9 +206,9 @@ render() {
return (
<div className="edu-back-white" >
<div className="educontent">
<div className="pt40 pb40">
<div className="clearfix mb30 shaiContent">
<span className="shaiTitle fl mt3">方向</span>
<div className="pt_b_26">
<div className="clearfix mb20 shaiContent">
<span className="shaiTitle fl">方向</span>
<div className="fl pr shaiAllItem">
<li className={shixunsearchAllvalue==="a"?"shaiItem shixun_repertoire active":"shaiItem shixun_repertoire"} value= "a" onClick={this.shixunsearchall}>全部</li>
<style>
@ -222,8 +222,8 @@ render() {
float: left;
margin-right: 20px;
color: #999;
cursor: pointer;
margin-bottom: 10px;
cursor: pointer;
margin-bottom:10px;
}
.ant-dropdown-menu-item, .ant-dropdown-menu-submenu-title{
padding: 0px 12px;
@ -250,12 +250,23 @@ render() {
</div>
</div>
<div className="clearfix">
<span className="shaiTitle fl mt6">筛选</span>
<span className="shaiTitle fl">筛选</span>
{
<style>
{`
{`
.shaiContent li.shaiItem{
padding:0px 15px;
line-height:32px;
height:32px
}
.shaiTitle{
height:32px;
line-height:32px;
}
.shaiItems{
padding: 3px 15px;
padding:0px 15px;
line-height:32px;
height:32px;
float: left;
border-radius: 4px;
color: #4C4C4C;

@ -10,7 +10,7 @@ import { Spin } from 'antd';
import { TPMIndexHOC } from '../TPMIndexHOC';
import { SnackbarHOC } from 'educoder';
import { SnackbarHOC,getImageUrl} from 'educoder';
import ShixunCardList from './ShixunCardList';
@ -389,6 +389,35 @@ class ShixunsIndex extends Component {
{this.state.updata===undefined?"":<UpgradeModals
{...this.state}
/>}
<style>
{
` ::-webkit-scrollbar {
width: 0px !important;
}
.myshixin-head{
width: 100%;
height: 240px;
background-image: url(${getImageUrl(this.props.mygetHelmetapi && this.props.mygetHelmetapi.shixun_banner_url === null ?`images/educoder/courses/courses.jpg`:this.props.mygetHelmetapi&&this.props.mygetHelmetapi.shixun_banner_url)});
background-color: #081C4B;
background-position: center;
background-repeat: no-repeat;
}
`
}
</style>
{
this.props.mygetHelmetapi&&this.props.mygetHelmetapi.shixun_banner_url?
<div className="myshixin-head pr" >
<div className="edu-txt-center pathNavLine">
<div className="inline path-nav">
</div>
</div>
</div>
:
""
}
{/*<Spin spinning={typepvisible} size="large" style={{marginTop:'15%'}}>*/}
<ShixunSearchBar
Updatasearchlist={this.Updatasearchlist.bind(this)}

@ -4,6 +4,9 @@
right: 3px;
top: 0px;
}
.pt_b_26{
padding:26px 0px;
}
.diffSelect{
margin-left:20px !important;

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe CreateStudentAttendanceRecordJob, type: :job do
pending "add some examples to (or delete) #{__FILE__}"
end

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