Merge branch 'topic_bank' into dev_hjm_banks

dev_aliyun_beta
hjm 5 years ago
commit 1650397e75

@ -1,21 +1,20 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-library-applies-index-page').length > 0) {
var $searchFrom = $('.library-applies-list-form');
$searchFrom.find('select[name="status"]').val('pending');
$searchFrom.on('click', '.search-form-tab', function(){
var $link = $(this);
if ($('body.admins-library-applies-index-page').length > 0) {
var $searchFrom = $('.library-applies-list-form');
$searchFrom.find('select[name="status"]').val('pending');
$searchFrom.find('input[name="keyword"]').val('');
$searchFrom.find('select[name="status"]').val('processed');
$searchFrom.on('click', '.search-form-tab', function(){
var $link = $(this);
if($link.data('value') === 'processed'){
$searchFrom.find('.status-filter').show();
} else {
$searchFrom.find('.status-filter').hide();
$searchFrom.find('select[name="status"]').val('pending');
}
});
$searchFrom.find('input[name="keyword"]').val('');
$searchFrom.find('select[name="status"]').val('processed');
}
if($link.data('value') === 'processed'){
$searchFrom.find('.status-filter').show();
} else {
$searchFrom.find('.status-filter').hide();
$searchFrom.find('select[name="status"]').val('pending');
}
});
}
})

@ -1,6 +1,6 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-shixun-settings-index-page').length > 0) {
$(".shixun-settings-select").on("change", function () {
$(".shixun-settings-list-container").on("change", '.shixun-settings-select', function () {
var s_value = $(this).val();
var s_name = $(this).attr("name");
var json = {};
@ -13,7 +13,7 @@ $(document).on('turbolinks:load', function() {
})
});
$(".shixun-setting-form").on("change",function () {
$(".shixun-settings-list-container").on("change", '.shixun-setting-form', function () {
var s_id = $(this).attr("data-id");
var s_value = $(this).val();
var s_name = $(this).attr("name");

@ -1,6 +1,8 @@
$(document).on('turbolinks:load', function() {
if($('body.admins-shixuns-index-page').length > 0){
$('select#tag-choosed').select2({
placeholder: "请选择分类",
allowClear: true
placeholder: "请选择分类",
allowClear: true
});
}
});

@ -0,0 +1,21 @@
//= require rails-ujs
//= require turbolinks
//= require jquery3
//= require popper
//= require bootstrap-sprockets
//= require echarts
//= require_tree ./colleges
Turbolinks.setProgressBarDelay(200);
$(document).on('turbolinks:load', function() {
$('[data-toggle="tooltip"]').tooltip();
$('[data-toggle="popover"]').popover();
})
$(document).on("turbolinks:before-cache", function () {
$('[data-toggle="tooltip"]').tooltip('hide');
$('[data-toggle="popover"]').popover('hide');
});

@ -0,0 +1,156 @@
$(document).on('turbolinks:load', function() {
if($('body.colleges-statistics-page').length > 0) {
var $statisticBody = $('.statistics-body');
var $statisticBase = $('.statistic-base');
var schoolId = $statisticBody.data('id');
var $statisticCourse = $statisticBody.find('.statistic-course')
var $shixunChart = $statisticBody.find('.shixun-chart');
$.get('/colleges/' + schoolId + '/shixun_time', function(data){
$statisticBase.find('.shixun-time').html("<span>" + data.shixun_time + "</span>天");
});
$.get('/colleges/' + schoolId + '/shixun_report_count', function(data){
$statisticBase.find('.shixun-report-count').html("<span>" + data.shixun_report_count + "</span>个");
});
$.ajax({ url: '/colleges/' + schoolId + '/course_statistics', method: 'GET', dataType: 'script' });
$.ajax({ url: '/colleges/' + schoolId + '/teachers', method: 'GET', dataType: 'script' });
var initShixunChart = function(names, data){
var shixunChart = echarts.init(document.getElementById('shixun-chart'));
var options = {
series : [
{
name: '访问来源',
type: 'pie',
radius: '55%',
data: data
}
]
};
shixunChart.setOption(options);
};
$.get('/colleges/' + schoolId + '/shixun_chart_data', function(data){
$statisticBody.find('.shixun-chart-loading').hide();
if (data.data.length > 0) {
$shixunChart.css('height', '400px').css('width', '100%');
initShixunChart(data.names, data.data);
} else {
$statisticBody.find('.shixun-chart-empty').show();
}
});
$.ajax({ url: '/colleges/' + schoolId + '/student_shixun', method: 'GET', dataType: 'script' });
var initHotEvaluating = function(names, values){
var Color = ['#962e66', '#623363', '#CCCCCC', '#9A9A9A', '#FF8080', '#FF80C2', '#B980FF', '#80B9FF', '#6FE9FF', '#4DE8B4', '#F8EF63', '#FFB967'];
var option = {
backgroundColor: '#fff',
grid: {
left: '3%',
right: '4%',
bottom: '10%',
containLabel: true
},
tooltip: {
show: "true",
trigger: 'item',
formatter: '{c0}',
backgroundColor: 'rgba(0,0,0,0.7)', // 背景
padding: [8, 10], //内边距
extraCssText: 'box-shadow: 0 0 3px rgba(255, 255, 255, 0.4);', //添加阴影
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
xAxis: {
type: 'value',
axisTick: {
show: false
},
axisLine: {
show: true,
lineStyle: {
color: '#CCCCCC'
}
},
splitLine: {
show: false,
lineStyle: {
color: '#CCCCCC'
}
},
axisLabel: {
textStyle: {
color: '#656565',
fontWeight: 'normal',
fontSize: '12'
},
formatter: '{value}'
}
},
yAxis: {
type: 'category',
axisLine: {
lineStyle: {
color: '#cccccc'
}
},
splitLine: {
show: false
},
axisTick: {
show: false
},
splitArea: {
show: false
},
axisLabel: {
inside: false,
textStyle: {
color: '#656565',
fontWeight: 'normal',
fontSize: '12'
}
},
data: names
},
series: [{
name: '',
type: 'bar',
itemStyle: {
normal: {
show: true,
color: function(params) {
return Color[params.dataIndex]
},
barBorderRadius: 50,
borderWidth: 0,
borderColor: '#333'
}
},
barGap: '0%',
barCategoryGap: '50%',
data: values
}
]
};
var myChart = echarts.init(document.getElementById('hot-chart'));
myChart.setOption(option);
}
$.get('/colleges/' + schoolId + '/student_hot_evaluations', function(data){
$statisticBody.find('.hot-chart-loading').hide();
if (data.names.length > 0) {
$statisticBody.find('.hot-chart').css('height', '400px').css('width', '100%');
initHotEvaluating(data.names.reverse(), data.values.reverse());
} else {
$statisticBody.find('.hot-chart-empty').show();
}
})
}
});

@ -7,6 +7,7 @@
@import "bootstrap-datepicker.standalone";
@import "lib/codemirror";
@import "common";
@import "admins/*";
body {
@ -20,20 +21,6 @@ body {
background: #efefef;
}
a {
&:hover {
text-decoration: unset;
}
}
textarea.danger, input.danger {
border-color: #dc3545!important;
}
label.error {
color: #dc3545!important;
}
.simple_form {
.form-group {
.collection_radio_buttons {
@ -50,9 +37,6 @@ input.form-control {
font-size: 14px;
}
.flex-1 {
flex: 1;
}
.btn-default{
color: #666;
background: #e1e1e1!important;
@ -62,8 +46,3 @@ input.form-control {
position: absolute;
}
.position-r{position:relative;}
.font-12 { font-size: 12px !important; }
.font-14 { font-size: 14px !important; }
.font-16 { font-size: 16px !important; }
.font-18 { font-size: 18px !important; }

@ -0,0 +1,13 @@
@import "bootstrap";
@import "font-awesome-sprockets";
@import "font-awesome";
@import "common";
@import "colleges/*";
.navbar-dark .navbar-nav .nav-link {
color: rgba(255, 255, 255, 1);
font-size: 16px;
}

@ -0,0 +1,135 @@
.colleges-statistics-page {
.college-body-container {
.statistic-header {
width: 100%;
height: 240px;
background-image: url('/images/educoder/statistics.jpg');
background-size: 100% 100%;
&-container {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
&-title {
flex: 1;
display: flex;
align-items: center;
color: #4CACFF;
font-size: 32px;
}
&-content {
width: 100%;
display: flex;
justify-content: space-around;
}
&-item {
margin-bottom: 22px;
display: flex;
flex-direction: column;
align-items: center;
color: #fff;
&-label {
color: #989898;
}
&-content {
font-size: 24px;
}
}
}
.statistic-box {
border: unset;
box-shadow: 0px 0px 9px rgba(174, 175, 177, 0.2);
}
.statistic-base {
&-title {
padding: 2rem 1.25rem;
background: #fff;
border-bottom: unset;
}
&-table {
margin: 0;
padding: 0;
}
&-item {
padding: 0;
&-label {
text-align: center;
font-size: 16px;
height: 48px;
line-height: 48px;
color: #686868;
background: #F5F5F5;
border-top: 1px solid #EBEBEB;
border-bottom: 1px solid #EBEBEB;
}
&-content {
height: 100px;
font-size: 16px;
text-align: center;
line-height: 100px;
span {
margin-right: 5px;
font-size: 24px;
}
}
}
}
.statistic-container {
padding: 0;
background: #fff;
border-radius: 3px;
box-shadow: 0px 0px 9px rgba(174, 175, 177, 0.2);
.statistic-label {
padding: 2rem 1.25rem;
font-size: 1.5rem;
}
.statistic-table {
overflow-x: scroll;
table.course-table { min-width: 1100px; }
table.teacher-rank-table { min-width: 640px; }
}
table th {
background: #F5F5F5;
border-color: #EBEBEB;
}
&.statistic-course {
min-height: 400px;
}
&.statistic-teacher-rank, &.statistic-student-rank {
min-height: 500px;
}
}
.statistic-chart {
padding: 0 20px;
height: 400px;
.shixun-chart-loading, .shixun-chart-empty, .hot-chart-loading, .hot-chart-empty {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
}
}
}

@ -0,0 +1,31 @@
body {
font-size: 14px;
background: #efefef;
}
a {
&:hover {
text-decoration: unset;
}
}
textarea.danger, input.danger {
border-color: #dc3545!important;
}
label.error {
color: #dc3545!important;
}
input.form-control {
font-size: 14px;
}
.flex-1 {
flex: 1;
}
.font-12 { font-size: 12px !important; }
.font-14 { font-size: 14px !important; }
.font-16 { font-size: 16px !important; }
.font-18 { font-size: 18px !important; }

@ -0,0 +1,171 @@
class CollegesController < ApplicationController
include Admins::PaginateHelper
layout 'college'
before_action :require_login
before_action :check_college_present!
before_action :check_manage_permission!
helper_method :current_school, :current_college
def statistics
# 教师、学生总数
count_statistic = UserExtension.where(school_id: current_school.id)
.select('SUM(IF(identity=0, 1, 0)) AS teachers_count, SUM(IF(identity=1, 1, 0)) AS students_count').first
@teachers_count = count_statistic['teachers_count']
@students_count = count_statistic['students_count']
# 课堂总数
@courses_count = Course.where(school_id: current_school.id, is_delete: 0).where.not(id: 1309).count
# 实训总数
@shixuns_count = Shixun.visible.joins('left join user_extensions on user_extensions.user_id = shixuns.user_id')
.where(user_extensions: { school_id: current_school.id }).count
end
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
render json: { shixun_time: shixun_time_sum }
end
def shixun_report_count
shixun_report_count = StudentWork.where(work_status: [1, 2]).where('myshixun_id != 0')
.joins('left join user_extensions on user_extensions.user_id = student_works.user_id')
.where(user_extensions: { school_id: current_school.id }).count
render json: { shixun_report_count: shixun_report_count }
end
def teachers
@teachers = User.find_by_sql("SELECT users.id, users.login, users.lastname, users.firstname, users.nickname, IFNULL((SELECT count(shixuns.id) FROM shixuns where shixuns.user_id =users.id group by shixuns.user_id), 0) AS publish_shixun_count,
(SELECT count(c.id) FROM courses c, course_members m WHERE c.id != 1309 and m.course_id = c.id AND m.role in (1,2,3) and c.school_id = #{current_school.id} AND m.user_id=users.id AND c.is_delete = 0) as course_count
FROM `users`, user_extensions ue where users.id=ue.user_id and ue.identity=0 and ue.school_id=#{current_school.id} ORDER BY publish_shixun_count desc, course_count desc, id desc LIMIT 10")
# ).order("publish_shixun_count desc, experience desc").limit(10)
@teachers =
@teachers.map do |teacher|
course_ids = Course.find_by_sql("SELECT c.id FROM courses c, course_members m WHERE c.id != 1309 and m.course_id = c.id AND m.role in (1,2,3) AND m.user_id=#{teacher.id} AND c.is_delete = 0 and c.school_id = #{current_school.id}")
course_count = course_ids.size
homeworks = HomeworkCommon.where(:homework_type => 4, :course_id => course_ids.map(&:id))
un_shixun_work_count = homeworks.where("publish_time > '#{Time.now}' or publish_time is null").count
shixun_work_count = homeworks.size - un_shixun_work_count
student_count = StudentsForCourse.where(:course_id => course_ids.map(&:id)).count
myshixun_ids = StudentWork.select("myshixun_id").where("homework_common_id in (#{homeworks.map(&:id).join(',').strip == "" ? -1 : homeworks.map(&:id).join(',')}) and myshixun_id is not null")
complete_myshixun = Myshixun.select("id").where(:status => 1, :id => myshixun_ids.map(&:myshixun_id)).size
all_myshixun = Myshixun.select("id").where(:id => myshixun_ids.map(&:myshixun_id)).size
complete_rate = all_myshixun == 0 ? 0 : ((complete_myshixun * 100) / all_myshixun).try(:round, 2).to_f
real_name = teacher.show_real_name
teacher = teacher.attributes.dup.merge({
real_name: real_name,
course_count: course_count,
shixun_work_count: shixun_work_count,
un_shixun_work_count: un_shixun_work_count,
student_count: student_count,
complete_rate: complete_rate
}).to_json
JSON.parse(teacher)
end
end
def shixun_chart_data
shixun_ids = HomeworkCommonsShixun.joins(homework_common: :course).where(courses: {school_id: current_school.id, is_delete: 0}).where('courses.id != 1309').pluck('distinct shixun_id')
shixun_count_map = ShixunTagRepertoire.joins(:tag_repertoire).where(shixun_id: shixun_ids).group('tag_repertoires.name').order('count_shixun_id desc').count(:shixun_id)
names = []
data = []
shixun_count_map.each do |name, count|
break if names.size == 9
names << name
data << { value: count, name: name }
end
if shixun_count_map.keys.size > 9
other_count = shixun_count_map.values[9..-1].reduce(:+)
names << 'Others'
data << { name: 'Others', value: other_count }
end
render json: { names: names, data: data }
end
# 在线课堂
def course_statistics
courses = Course.where(school_id: current_school.id, is_delete: 0).where.not(id: 1309)
courses = courses.left_joins(practice_homeworks: { student_works: { myshixun: :games } })
.select('courses.id, courses.name, courses.is_end, sum(games.evaluate_count) evaluating_count')
.group('courses.id').order('is_end asc, evaluating_count desc')
params[:per_page] = 8
@courses = paginate courses
course_ids = @courses.map(&:id)
@student_count = StudentsForCourse.where(course_id: course_ids).group(:course_id).count
@shixun_work_count = HomeworkCommon.where(homework_type: 4, course_id: course_ids).group(:course_id).count
@attachment_count = Attachment.where(container_id: course_ids, container_type: 'Course').group(:container_id).count
@message_count = Message.joins(:board).where(boards: { parent_id: 0, course_id: course_ids }).group('boards.course_id').count
@active_time = CourseActivity.where(course_id: course_ids).group(:course_id).maximum(:created_at)
@exercise_count = Exercise.where(course_id: course_ids).group(:course_id).count
@poll_count = Poll.where(course_id: course_ids).group(:course_id).count
@other_work_count = HomeworkCommon.where(homework_type: [1,3], course_id: course_ids).group(:course_id).count
end
# 学生实训
def student_shixun
@students = User.joins(:user_extension).where(user_extensions: { school_id: current_school.id, identity: 1 }).includes(:user_extension).order('experience desc').limit(10)
student_ids = @students.map(&:id)
@shixun_count = Myshixun.where(user_id: student_ids).group(:user_id).count
@study_shixun_count = Myshixun.where(user_id: student_ids, status: 0).group(:user_id).count
end
def student_hot_evaluations
games = Game.joins(:myshixun).joins('join shixun_tag_repertoires str on str.shixun_id = myshixuns.shixun_id')
games = games.joins('join tag_repertoires tr on tr.id = str.tag_repertoire_id')
games = games.joins("join user_extensions ue on ue.user_id = myshixuns.user_id and ue.school_id = #{current_school.id}")
evaluate_count_map = games.group('tr.name').reorder('sum_games_evaluate_count desc').limit(10).sum('games.evaluate_count')
render json: { names: evaluate_count_map.keys, values: evaluate_count_map.values }
end
private
def require_login
return if User.current.logged?
redirect_to "/login?back_url=#{CGI::escape(request.fullpath)}"
end
def check_college_present!
return if current_college.present?
redirect_to '/404'
end
def check_manage_permission!
return if can_manage_college?
redirect_to '/403'
end
def can_manage_college?
return true if current_user.admin_or_business? # 超级管理员|运营
return true if current_college.is_a?(Department) && current_college.member?(current_user) # 部门管理员
return true if current_user.is_teacher? && current_user.school_id == current_school.id # 学校老师
return true if current_school.customer_id && current_user.partner&.partner_customers&.exists?(customer_id: current_school.customer_id)
false
end
def current_school
current_college.is_a?(School) ? current_college : current_college.school
end
def current_college
@_current_college ||= begin
Department.find_by(identifier: params[:id]) || School.find_by(id: params[:id])
end
end
end

@ -10,7 +10,6 @@ module GitCommon
# ------------------------
# 版本库目录结构
def repository
logger.info("ssssssseeeeeeee#{params}")
begin
@repo_url = repo_url @repo_path
@trees = GitService.file_tree(repo_path: @repo_path, path: @path)
@ -44,4 +43,16 @@ module GitCommon
end
end
# 为版本库添加文件
def add_file
@path, message, content = params[:path].strip, params[:message], params[:content]
author_name, author_email = current_user.real_name, current_user.current_user.git_mail
@content = GitService.update_file(repo_path: @repo_path,
file_path: path,
message: message,
content: content,
author_name: author_name,
author_email: author_email)
end
end

@ -1085,11 +1085,15 @@ class CoursesController < ApplicationController
# 导出课堂信息
def export_couser_info
set_export_cookies
course_info_to_xlsx @course
filename_ = "#{current_user.real_name}_#{@course.name}_课堂信息_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
render xlsx: "#{format_sheet_name filename_.strip}",template: "courses/export_course_info.xlsx.axlsx",
locals: {course_info: @course_info}
if params[:export].present? && params[:export]
normal_status(0,"正在下载中")
else
set_export_cookies
course_info_to_xlsx @course
filename_ = "#{current_user.real_name}_#{@course.name}_课堂信息_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
render xlsx: "#{format_sheet_name filename_.strip}",template: "courses/export_course_info.xlsx.axlsx",
locals: {course_info: @course_info}
end
end
# 导出活跃度

@ -372,7 +372,7 @@ class ExerciseBankQuestionsController < ApplicationController
private
def bank_admin
tip_exception(403, "无权限") unless (current_user.certification_teacher? && @bank.user_id == current_user.id) || current_user.admin?
tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin?
end
def get_exercise

@ -8,44 +8,18 @@ class ExerciseBanksController < ApplicationController
def show
@exercise_questions = @bank.exercise_bank_questions&.includes(:exercise_bank_choices, :exercise_bank_shixun_challenges,
:exercise_bank_standard_answers).order("question_number ASC")
@exercise_ques_count = @exercise_questions.size # 全部的题目数
@exercise_ques_scores = @exercise_questions.pluck(:question_score).sum
#单选题的数量及分数
exercise_single_ques = @exercise_questions.find_by_custom("question_type", Exercise::SINGLE)
@exercise_single_ques_count = exercise_single_ques.size
@exercise_single_ques_scores = exercise_single_ques.pluck(:question_score).sum
#多选题的数量及分数
exercise_double_ques = @exercise_questions.find_by_custom("question_type", Exercise::MULTIPLE)
@exercise_double_ques_count = exercise_double_ques.size
@exercise_double_ques_scores = exercise_double_ques.pluck(:question_score).sum
# 判断题数量及分数
exercise_ques_judge = @exercise_questions.find_by_custom("question_type", Exercise::JUDGMENT)
@exercise_ques_judge_count = exercise_ques_judge.size
@exercise_ques_judge_scores = exercise_ques_judge.pluck(:question_score).sum
#填空题数量及分数
exercise_ques_null = @exercise_questions.find_by_custom("question_type", Exercise::COMPLETION)
@exercise_ques_null_count = exercise_ques_null.size
@exercise_ques_null_scores = exercise_ques_null.pluck(:question_score).sum
#简答题数量及分数
exercise_ques_main = @exercise_questions.find_by_custom("question_type", Exercise::SUBJECTIVE)
@exercise_ques_main_count = exercise_ques_main.size
@exercise_ques_main_scores = exercise_ques_main.pluck(:question_score).sum
#实训题数量及分数
exercise_ques_shixun = @exercise_questions.find_by_custom("question_type", Exercise::PRACTICAL)
@exercise_ques_shixun_count = exercise_ques_shixun.size
@exercise_ques_shixun_scores = exercise_ques_shixun.pluck(:question_score).sum
if @bank.container_type == "Exercise"
get_exercise_question_count
else
get_poll_question_count
end
end
def update
tip_exception("试卷标题不能为空!") if params[:exercise_name].blank?
tip_exception("标题不能为空!") if params[:exercise_name].blank?
@bank.update_attributes!(name: params[:exercise_name], description: params[:exercise_description])
normal_status(0,"试卷更新成功")
normal_status(0,"更新成功")
end
def choose_shixun
@ -84,11 +58,12 @@ class ExerciseBanksController < ApplicationController
def find_bank
@bank = ExerciseBank.find_by!(id: params[:id])
tip_exception(403, "无权限") unless (current_user.certification_teacher? && (@bank.is_public || @bank.user_id == current_user.id)) || current_user.admin?
tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin? ||
(current_user.certification_teacher? && @bank.is_public)
end
def bank_admin
tip_exception(403, "无权限") unless (current_user.certification_teacher? && @bank.user_id == current_user.id) || current_user.admin?
tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin?
end
#判断实训是否已选择
@ -103,4 +78,46 @@ class ExerciseBanksController < ApplicationController
end
end
def get_exercise_question_count
@exercise_ques_count = @exercise_questions.size # 全部的题目数
@exercise_ques_scores = @exercise_questions.pluck(:question_score).sum
#单选题的数量及分数
exercise_single_ques = @exercise_questions.find_by_custom("question_type", Exercise::SINGLE)
@exercise_single_ques_count = exercise_single_ques.size
@exercise_single_ques_scores = exercise_single_ques.pluck(:question_score).sum
#多选题的数量及分数
exercise_double_ques = @exercise_questions.find_by_custom("question_type", Exercise::MULTIPLE)
@exercise_double_ques_count = exercise_double_ques.size
@exercise_double_ques_scores = exercise_double_ques.pluck(:question_score).sum
# 判断题数量及分数
exercise_ques_judge = @exercise_questions.find_by_custom("question_type", Exercise::JUDGMENT)
@exercise_ques_judge_count = exercise_ques_judge.size
@exercise_ques_judge_scores = exercise_ques_judge.pluck(:question_score).sum
#填空题数量及分数
exercise_ques_null = @exercise_questions.find_by_custom("question_type", Exercise::COMPLETION)
@exercise_ques_null_count = exercise_ques_null.size
@exercise_ques_null_scores = exercise_ques_null.pluck(:question_score).sum
#简答题数量及分数
exercise_ques_main = @exercise_questions.find_by_custom("question_type", Exercise::SUBJECTIVE)
@exercise_ques_main_count = exercise_ques_main.size
@exercise_ques_main_scores = exercise_ques_main.pluck(:question_score).sum
#实训题数量及分数
exercise_ques_shixun = @exercise_questions.find_by_custom("question_type", Exercise::PRACTICAL)
@exercise_ques_shixun_count = exercise_ques_shixun.size
@exercise_ques_shixun_scores = exercise_ques_shixun.pluck(:question_score).sum
end
def get_poll_question_count
@poll_questions_count = @exercise_questions&.size # 全部的题目数
@poll_question_singles = @exercise_questions.find_by_custom("question_type", 1).size # 单选题
@poll_question_doubles = @exercise_questions.find_by_custom("question_type", 2).size # 多选题
@poll_question_mains = @exercise_questions.find_by_custom("question_type", 3).size #主观题
end
end

@ -255,8 +255,8 @@ class ExerciseQuestionsController < ApplicationController
end
elsif @exercise_question.question_type == Exercise::COMPLETION #填空题
old_ex_answer = @exercise_question.exercise_standard_answers #当前问题的全部标准答案
old_ex_answer_choice_texts = old_ex_answer.pluck(:answer_text).uniq.sort
new_ex_answer_choice_texts = standard_answer.pluck(:answer_text).sum.uniq.sort
old_ex_answer_choice_texts = old_ex_answer.pluck(:answer_text).sort
new_ex_answer_choice_texts = standard_answer.pluck(:answer_text).sum.sort
if old_ex_answer_choice_texts != new_ex_answer_choice_texts #填空题标准答案有更改时,才会更新标准答案
new_ex_answer_choice_ids = standard_answer.map {|a| a[:choice_id]}.uniq #新传入的答案数组序号
old_ex_answer_choice_ids = old_ex_answer.pluck(:exercise_choice_id).uniq #全部的答案数组序号
@ -280,11 +280,12 @@ class ExerciseQuestionsController < ApplicationController
if null_choice_text_count >= ex_answer_pre_count
new_add_choice = null_choice_text_count_array - ex_answer_pre_count_array
ex_answer_pre_count_array.each do |n|
standard_option = {
:exercise_question_id => @exercise_question.id,
:exercise_choice_id => null_choice_id,
:answer_text => null_choice_text[n-1]
@hash_symbol_null_ = {
:exercise_question_id => @exercise_question.id,
:exercise_choice_id => null_choice_id,
:answer_text => null_choice_text[n - 1]
}
standard_option = @hash_symbol_null_
ex_answer_pre[n-1].update(standard_option)
end
if new_add_choice.count > 0 #表示有新增的
@ -680,8 +681,9 @@ class ExerciseQuestionsController < ApplicationController
normal_status(-1,"已发布/已截止,不允许增删答案!")
elsif standard_answer.present?
if @exercise_question.question_type == Exercise::COMPLETION
exercise_answers_text = standard_answer.map{|a| a[:answer_text]}.sum.uniq
unless (standard_answer.count == exercise_choice_ids.count) && (standard_answers_text.count == exercise_answers_text.count)
# exercise_answers_text = standard_answer.map{|a| a[:answer_text]}.sum.uniq
# unless (standard_answer.count == exercise_choice_ids.count) && (standard_answers_text.count == exercise_answers_text.count)
unless standard_answer.count == exercise_choice_ids.count
normal_status(-1,"已发布/已截止,不允许增删标准答案!")
end
elsif @exercise_question.question_type == Exercise::SUBJECTIVE

@ -392,59 +392,6 @@ class GamesController < ApplicationController
end
end
# # 文件更新;数据评测记录
# # 生成重新评测认证码
# # content_modified:0 表示文件没有更新content_modified:1 表示文件有更新
# def file_update
# path = params[:path].strip unless params[:path].blank?
# myshixun = @game.myshixun
# rev = params[:rev] ? params[:rev] : "master"
# @content_modified = 0
# # params[:evaluate] 实训评测时更新必须给的参数,需要依据该参数做性能统计,其它类型的更新可以跳过
# # 自动保存的时候evaluate为0点评测的时候为1
# if params[:evaluate] == 1
# record = EvaluateRecord.create!(:user_id => current_user.id, :shixun_id => myshixun.shixun_id, :game_id => @game.id)
# uid_logger("-- game is #{@game.id}, record id is #{record.id}, time is **** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
# student_work_time = format("%.3f", (Time.now.to_f - record.created_at.to_f)).to_f
# record.update_attributes!(:student_work => student_work_time)
# end
# # 远程版本库文件内容
# last_content = GitService.file_content(repo_path: @repo_path, path: path)["content"]
# last_content = tran_base64_decode64(last_content)
#
# content = if @myshixun.mirror_name.select{|a| a.include?("MachineLearning") || a.include?("Python")}.present? && params[:content].present?
# params[:content].gsub(/\t/, ' ')
# else
# params[:content]
# end
# if content != last_content
# @content_modified = 1
# code_file = @g.edit_file(myshixun.gpid, current_user.login, :content => content, :file_path => path,
# :branch_name => rev, :commit_message => params[:evaluate] == 0 ? "auto commit" : "task commit")
# uid_logger("-- file update #{code_file}")
# # REDO更新失败的处理
# raise("文件更新失败") unless code_file
# end
#
# if record.present?
# consume_time = format("%.3f", (Time.now.to_f - record.created_at.to_f)).to_f
# record.update_attributes!(:file_update => consume_time)
# end
#
# # status为2说明是重新评测
# if @game.status == 2
# code = CODES.sample(8).join
# @resubmit = "#{code}_#{@myshixun.id}"
# end
#
# if content != last_content && code_file.blank?
# raise("实训平台繁忙繁忙等级81请稍后刷新并重试")
# end
# rescue Exception => e
# uid_logger("-- file update failed #{e.message}")
# raise Educoder::TipException.new("#{e.message}")
# end
# 恢复初始代码
# 注意path为当前打开文件的path
def reset_original_code

@ -23,11 +23,12 @@ class GtopicBanksController < ApplicationController
def find_bank
@bank = GtopicBank.find_by!(id: params[:id])
tip_exception(403, "无权限") unless (current_user.certification_teacher? && (@bank.is_public || @bank.user_id == current_user.id)) || current_user.admin?
tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin? ||
(current_user.certification_teacher? && @bank.is_public)
end
def bank_admin
tip_exception(403, "无权限") unless (current_user.certification_teacher? && @bank.user_id == current_user.id) || current_user.admin?
tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin?
end
def gtopic_bank_params

@ -39,11 +39,12 @@ class HomeworkBanksController < ApplicationController
def find_bank
@bank = HomeworkBank.find_by!(id: params[:id])
tip_exception(403, "无权限") unless (current_user.certification_teacher? && (@bank.is_public || @bank.user_id == current_user.id)) || current_user.admin?
tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin? ||
(current_user.certification_teacher? && @bank.is_public)
end
def bank_admin
tip_exception(403, "无权限") unless (current_user.certification_teacher? && @bank.user_id == current_user.id) || current_user.admin?
tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin?
end
def bank_params

@ -0,0 +1,157 @@
class PollBankQuestionsController < ApplicationController
before_action :require_login, :check_auth #用户需登陆
before_action :get_poll, only:[:create] #获取试卷
before_action :get_poll_question, except: [:create] #获取试卷的问题及试卷
before_action :bank_admin #是否为老师
before_action :validates_params, only: [:create, :update] #传入参数的验证
def create
ActiveRecord::Base.transaction do
begin
poll_options = {
:question_title => params[:question_title],
:question_type => params[:question_type],
:is_necessary => params[:is_necessary].to_i,
:question_number => @poll.exercise_bank_questions.count + 1,
:max_choices => params[:max_choices] || nil,
:min_choices => params[:min_choices] || nil
}
@poll_question = @poll.exercise_bank_questions.new(poll_options)
if params[:insert_id].present? #插入问题时那么从插入的这个id以后的question_num都将要+1
insert_poll = @poll.exercise_bank_questions.find_by(id: params[:insert_id])
if insert_poll.present? #如果该问题存在的话,意思是如果是第一题,那么就不存在插入
ques_num = insert_poll.question_number.to_i
@poll_question.question_number = ques_num + 1 #更新了问题的位置
@poll.exercise_bank_questions.insert_question_ex(ques_num).update_all("question_number = question_number + 1")
end
end
if @poll_question.save!
if params[:question_type] != 3
p_answer = params[:question_answers]
p_other_answer = params[:question_other_answer]
# 新增选择题答案选择的选项
(1..p_answer.count).each do |i|
answer = p_answer[i-1] # 传入的答案的内容
question_option = {
:choice_position => i,
:choice_text => answer
}
poll_answers = @poll_question.exercise_bank_choices.new question_option
poll_answers.save
end
# 新增答案的其他选项
if p_other_answer
question_option = {
:choice_position => p_answer.count + 1,
:choice_text => ''
}
poll_answers = @poll_question.exercise_bank_choices.new question_option
poll_answers.save
end
end
end
normal_status("创建成功")
rescue Exception => e
uid_logger_error(e.message)
tip_exception("问卷的问题创建失败!")
raise ActiveRecord::Rollback
end
end
end
def update
ActiveRecord::Base.transaction do
begin
if @poll_question.question_type < 3 #当为单选题或多选题时
p_answer = params[:question_answers]
p_other_answer = params[:question_other_answer]
p_answer_count = p_answer.count
@poll_question.exercise_bank_choices.each do |an|
if (p_answer_count < @poll_current_answers) && (p_answer_count..@poll_current_answers).to_a.include?(an.choice_position)
an.destroy
end
end
(1..p_answer_count).each do |i|
answer = @poll_question.exercise_bank_choices.find_by_custom("choice_position",i).first
if answer # 判断该位置的answer是否存在存在则更新.不存在则跳到下一步
answer.choice_text = p_answer[i-1]
answer.choice_position = i
answer.save
else
answer_options = {
:choice_position => i,
:choice_text => p_answer[i-1]
}
@poll_question.exercise_bank_choices.new answer_options
end
end
if p_other_answer #判断答案的其他选项是否存在
other_answer = @poll_question.exercise_bank_choices.find_by_custom("choice_text","").first
if other_answer.blank?
question_option = {
:choice_position => p_answer_count + 1,
:choice_text => ''
}
@poll_question.exercise_bank_choices.new question_option
else
other_answer.choice_position = p_answer_count + 1
other_answer.save
end
end
end
@poll_question.update_attributes(poll_questions_params)
normal_status("问卷更新成功")
rescue Exception => e
uid_logger_error(e.message)
tip_exception("更新失败")
raise ActiveRecord::Rollback
end
end
end
private
def bank_admin
tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin?
end
def get_poll
@poll = ExerciseBank.find_by!(id: params[:exercise_bank_id])
end
def get_poll_question
@poll_question = ExerciseBankQuestion.find_by!(id: params[:id])
@poll = @poll_question.exercise_bank
end
def poll_questions_params
params.require(:poll_question).permit(:question_title,:question_type,:is_necessary,:question_number,:max_choices,:min_choices)
end
def validates_params
normal_status(-1, "问题标题不能为空!") if params[:question_title].blank?
normal_status(-1, "是否要求必答的值不能为空!") if params[:is_necessary].blank?
normal_status(-1, "问题类型不能为空!") if params[:question_type].blank?
if params[:min_choices].present? && params[:max_choices].present? && (params[:min_choices].to_i > params[:max_choices].to_i)
normal_status(-1, "最小可选不能大于最大可选!")
elsif params[:question_answers].present? && (params[:max_choices].to_i > params[:question_answers].count)
normal_status(-1, "选择题的最大可选项不能大于答案数!")
elsif [1,3].include?(params[:question_type]) && (params[:max_choices].to_i > 0 || params[:min_choices].to_i > 0)
normal_status(-1, "单选题或主观题不能有最大或最小选择数!")
elsif params[:question_type] == 3 && (params[:question_answers] || params[:question_other_answer])
normal_status(-1, "主观问题不需要可选答案!")
elsif params[:question_type] != 3
if params[:question_answers].present? && params[:question_answers].include?("")
normal_status(-1, "选择题不能有空值!")
elsif params[:question_other_answer].present? && params[:question_other_answer].length > 0
normal_status(-1, "其他选项不能有值!")
elsif params[:question_type] == 1 && params[:question_answers].count < 2
normal_status(-1, "单选题选项不能小于2")
elsif params[:question_type] == 2 && params[:question_answers].count < 3
normal_status(-1, "多选题选项不能小于3")
end
end
end
end

@ -901,9 +901,9 @@ class PollsController < ApplicationController
error_question = []
@poll_multi_questions.each do |q|
poll_user_votes = current_user.poll_votes.where(poll_question_id:q.id)&.size
if q.max_choices.present? && (poll_user_votes > q.max_choices)
if q.max_choices.present? && (q.max_choices > 0) && (poll_user_votes > q.max_choices)
error_messages = "#{q.question_number}题:超过最大选项限制"
elsif q.min_choices.present? && (poll_user_votes < q.min_choices)
elsif q.min_choices.present? && (q.min_choices > 0)&& (poll_user_votes < q.min_choices)
error_messages = "#{q.question_number}题:不得少于最小选项限制"
else
error_messages = nil
@ -936,7 +936,7 @@ class PollsController < ApplicationController
def commit_result
ActiveRecord::Base.transaction do
begin
@poll_users = @poll.all_poll_users(current_user.id)
@poll_users = @poll.all_poll_users(current_user.id).where(commit_status:1) # 问卷已提交的用户
@poll_commit_ids = @poll_users.commit_by_status(1).pluck(:user_id) #问卷提交用户的id
@page = params[:page] || 1
@limit = params[:limit] || 10

@ -1,7 +1,7 @@
class QuestionBanksController < ApplicationController
before_action :require_login, :check_auth
before_action :params_filter, except: [:my_courses]
before_action :teacher_or_admin, except: [:bank_list]
# before_action :teacher_or_admin, except: [:bank_list]
# 题库选用列表
# object_type: # normal 普通作业题库; group 分组作业题库; poll问卷题库 exercise试卷题库; gtask 毕设选题题库gtopic 毕设任务

@ -25,11 +25,12 @@ class TaskBanksController < ApplicationController
def find_bank
@bank = GtaskBank.find_by!(id: params[:id])
tip_exception(403, "无权限") unless (current_user.certification_teacher? && (@bank.is_public || @bank.user_id == current_user.id)) || current_user.admin?
tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin? ||
(current_user.certification_teacher? && @bank.is_public)
end
def bank_admin
tip_exception(403, "无权限") unless (current_user.certification_teacher? && @bank.user_id == current_user.id) || current_user.admin?
tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin?
end
def gtask_bank_params

@ -18,8 +18,8 @@ class Users::QuestionBanksController < Users::BaseController
def load_question_banks_solve_count
question_bank_ids = @question_banks.map(&:id)
@solve_count_map =
case params[:category]
when 'common', 'group' then
case params[:object_type]
when 'normal', 'group' then
StudentWork.where(is_delete: false, work_status: [1, 2, 3]).joins(:homework_common)
.where(homework_commons: { homework_bank_id: question_bank_ids })
.group('homework_commons.homework_bank_id').count
@ -42,14 +42,14 @@ class Users::QuestionBanksController < Users::BaseController
end
def query_params
params.permit(:type, :category, :course_list_id, :sort_by, :sort_direction)
params.permit(:type, :object_type, :course_list_id, :sort_by, :sort_direction)
end
def check_query_params!
params[:type] = 'personal' if params[:type].blank? || !%w(personal publicly).include?(params[:type])
if params[:category].blank? || !%w(common group exercise poll gtask gtopic).include?(params[:category])
params[:category] = 'common'
if params[:object_type].blank? || !%w(normal group exercise poll gtask gtopic).include?(params[:object_type])
params[:object_type] = 'normal'
end
if params[:sort_by].blank? || !%w(updated_at name contributor).include?(params[:sort_by])

@ -0,0 +1,10 @@
class Customer < ApplicationRecord
default_scope { order(created_at: :desc) }
belongs_to :school
has_many :partner_customers, dependent: :destroy
has_many :partners, through: :partner_customers
has_many :users
end

@ -9,6 +9,10 @@ class Department < ApplicationRecord
scope :without_deleted, -> { where(is_delete: false) }
def member?(user)
department_members.exists?(user_id: user.id)
end
def soft_delete!
update!(is_delete: true)
end

@ -3,7 +3,7 @@ class HomeworkCommon < ApplicationRecord
enum homework_type: { normal: 1, program: 2, group: 3, practice: 4 }, _suffix: true
has_many :homework_group_settings, dependent: :destroy
has_many :published_settings, -> { group_published }, class_name: "HomeworkGroupSetting"
has_many :student_works, -> { where("is_delete = 0") }
has_many :student_works, -> { where(is_delete: 0) }
has_many :score_student_works, -> { where("is_delete = 0 and work_status != 0").order("work_score desc") }, class_name: "StudentWork"
has_one :homework_detail_manual, dependent: :destroy

@ -1,3 +1,5 @@
class Partner < ApplicationRecord
belongs_to :school, optional: true
has_many :users
end

@ -0,0 +1,4 @@
class PartnerCustomer < ApplicationRecord
belongs_to :partner
belongs_to :customer
end

@ -13,6 +13,9 @@ class School < ApplicationRecord
has_many :school_daily_reports, dependent: :destroy
has_many :courses
has_many :customers, dependent: :destroy
has_many :partners, dependent: :destroy
# 学校管理员
def manager?(user)
ec_school_users.exists?(user_id: user.id)

@ -139,7 +139,7 @@ class User < ApplicationRecord
has_many :videos, dependent: :destroy
# 客户管理
belongs_to :partner
belongs_to :partner, optional: true
# Groups and active users
scope :active, lambda { where(status: STATUS_ACTIVE) }

@ -15,7 +15,7 @@ class Admins::DepartmentQuery < ApplicationQuery
keyword = params[:keyword].to_s.strip
if keyword.present?
departments = departments.joins(:school)
.where('schools.name LIKE :keyword OR departments.name LIKE :keyword', keyword: keyword)
.where('schools.name LIKE :keyword OR departments.name LIKE :keyword', keyword: "%#{keyword}%")
end
if params[:with_member].to_s == 'true'

@ -22,8 +22,8 @@ class Users::QuestionBankService
course_lists = CourseList.joins(relation_name).where.not(relation_name => { id: nil })
category_condition =
case params[:category]
when 'common' then { homework_type: 1 }
case params[:object_type]
when 'normal' then { homework_type: 1 }
when 'group' then { homework_type: 3 }
when 'exercise' then { container_type: 'Exercise' }
when 'poll' then { container_type: 'Poll' }
@ -47,8 +47,8 @@ class Users::QuestionBankService
def class_name
@_class_name ||= begin
case params[:category]
when 'common', 'group' then 'HomeworkBank'
case params[:object_type]
when 'normal', 'group' then 'HomeworkBank'
when 'exercise', 'poll' then 'ExerciseBank'
when 'gtask' then 'GtaskBank'
when 'gtopic' then 'GtopicBank'
@ -58,8 +58,8 @@ class Users::QuestionBankService
end
def category_filter(relations)
case params[:category]
when 'common' then
case params[:object_type]
when 'normal' then
relations.where(homework_type: 1)
when 'group' then
relations.where(homework_type: 3)

@ -14,7 +14,13 @@
<td class="member-container">
<%= render partial: 'admins/departments/shared/member_users', locals: { department: department } %>
</td>
<td><%= link_to department.identifier.to_s, '#', target: '_blank' %></td>
<td>
<% if department.identifier.present? %>
<%= link_to department.identifier.to_s, statistics_college_path(department.identifier), target: '_blank' %>
<% else %>
--
<% end %>
</td>
<td><%= department.host_count %></td>
<td><%= department.created_at&.strftime('%Y-%m-%d %H:%M') %></td>
<td class="action-container">

@ -0,0 +1,45 @@
<table class="table text-center course-table">
<thead class="thead-light">
<tr>
<th class="text-left">名称</th>
<th class="text-left">管理教师</th>
<th>评测次数</th>
<th>学生</th>
<th>实训作业</th>
<th>资源</th>
<th>帖子</th>
<th>其它任务</th>
<th>状态</th>
<th>时间</th>
</tr>
</thead>
<tbody>
<% if @courses.present? %>
<% @courses.each do |course| %>
<tr>
<td class="text-left"><a href="/courses/<%= course.id %>/students" target="_blank" class="d-inline-block text-truncate" style="max-width: 220px"><%= course.name %></a></td>
<% teacher_names = course.teacher_users.map(&:real_name).join('、') %>
<td class="text-left">
<span class="d-inline-block text-truncate" style="max-width: 150px" data-toggle="tooltip" title="<%= teacher_names %>">
<%= teacher_names || '--' %>
</span>
</td>
<td><%= course.evaluating_count.to_i %></td>
<td><%= @student_count.fetch(course.id, 0) %></td>
<td><%= @shixun_work_count.fetch(course.id, 0) %></td>
<td><%= @attachment_count.fetch(course.id, 0) %></td>
<td><%= @message_count.fetch(course.id, 0) %></td>
<td><%= @exercise_count.fetch(course.id, 0) + @poll_count.fetch(course.id, 0) + @other_work_count.fetch(course.id, 0) %></td>
<td class="<%= course.is_end ? 'text-secondary' : 'text-warning' %>"><%= course.is_end ? "已结束" : "正在进行" %></td>
<td class="text-secondary"><%= @active_time[course.id]&.strftime('%Y-%m-%d %H:%M') %></td>
</tr>
<% end %>
<% else %>
<tr><td colspan="100">暂无数据</td></tr>
<% end %>
</tbody>
</table>
<div class="d-flex justify-content-center text-center">
<%= render partial: 'admins/shared/paginate', locals: { objects: @courses } %>
</div>

@ -0,0 +1,23 @@
<% if @students.present? %>
<% @students.each_with_index do |student, index| %>
<tr>
<td>
<% if index < 3 %>
<img src="/images/educoder/competition/<%= index + 1 %>.png" width="18px" height="22px" class="mt8"/></td>
<% else %>
<%= index + 1 %>
<% end %>
</td>
<td class="color-dark">
<a href="/users/<%= student.login %>" target="_blank" class="d-inline-block text-truncate" style="max-width: 84px;"><%= student.real_name %></a>
</td>
<td><%= student.student_id %></td>
<td><%= @shixun_count.fetch(student.id, 0) %></td>
<td><%= @study_shixun_count.fetch(student.id, 0) %></td>
<td><%= student.grade %></td>
<td class="text-info"><%= student.experience %></td>
</tr>
<% end %>
<% else %>
<tr><td colspan="100">暂无数据</td></tr>
<% end %>

@ -0,0 +1,21 @@
<% if @teachers.present? %>
<% @teachers.each_with_index do |teacher, index| %>
<tr>
<td class="pl20 pr20">
<% if index < 3 %>
<img src="/images/educoder/competition/<%= index + 1 %>.png" width="18px" height="22px" class="mt8"/></td>
<% else %>
<%= index + 1 %>
<% end %>
<td class="color-dark"><a href="<%= user_path(teacher['login']) %>" target="_blank" class="task-hide" style="max-width: 84px;"><%= teacher['real_name'] %></a></td>
<td><%= teacher['course_count'] %></td>
<td><%= teacher['shixun_work_count'] %></td>
<td><%= teacher['un_shixun_work_count'] %></td>
<td><%= teacher['student_count'] %></td>
<td><%= teacher['complete_rate'] %>%</td>
<td class="color-blue"><%= teacher['publish_shixun_count'].to_i %></td>
</tr>
<% end %>
<% else %>
<tr><td colspan="100">暂无数据</td></tr>
<% end %>

@ -0,0 +1 @@
$(".statistic-course .statistic-table").html("<%= j(render 'colleges/course_statistics') %>");

@ -0,0 +1,21 @@
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<a class="navbar-brand" href="#">
<img src="/images/educoder/headNavLogo.png" width="40" height="40" alt="">
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item"><a class="nav-link" href="/paths">实践课程</a></li>
<li class="nav-item"><a class="nav-link" href="/courses">翻转课堂</a></li>
<li class="nav-item"><a class="nav-link" href="/shixuns">实训项目</a></li>
<li class="nav-item"><a class="nav-link" href="/competitions">在线竞赛</a></li>
<li class="nav-item"><a class="nav-link" href="/moop_cases">教学案例</a></li>
<li class="nav-item"><a class="nav-link" href="/crowdsourcing">众包创新</a></li>
<li class="nav-item"><a class="nav-link" href="/forums">交流问答</a></li>
</ul>
</div>
</nav>

@ -0,0 +1,164 @@
<div class="statistic-header">
<div class="container statistic-header-container">
<div class="statistic-header-title"><%= current_school.name %></div>
<div class="statistic-header-content">
<div class="statistic-header-item">
<div class="statistic-header-item-label">教师</div>
<div class="statistic-header-item-content"><%= @teachers_count %></div>
</div>
<div class="statistic-header-item">
<div class="statistic-header-item-label">学生</div>
<div class="statistic-header-item-content"><%= @students_count %></div>
</div>
<div class="statistic-header-item">
<div class="statistic-header-item-label">课堂</div>
<div class="statistic-header-item-content"><%= @courses_count %></div>
</div>
<div class="statistic-header-item">
<div class="statistic-header-item-label">共建实训</div>
<div class="statistic-header-item-content"><%= @shixuns_count %></div>
</div>
</div>
</div>
</div>
<div class="container statistics-body my-4" data-id="<%= current_school.id %>">
<div class="card statistic-box statistic-base">
<h4 class="card-header statistic-base-title">基本情况</h4>
<div class="card-body statistic-base-table row">
<div class="col-4 col-md-2 statistic-base-item">
<div class="statistic-base-item-label">教师</div>
<div class="statistic-base-item-content">
<span><%= @teachers_count %></span>人
</div>
</div>
<div class="col-4 col-md-2 statistic-base-item">
<div class="statistic-base-item-label">学生</div>
<div class="statistic-base-item-content">
<span><%= @students_count %></span>人
</div>
</div>
<div class="col-4 col-md-2 statistic-base-item">
<div class="statistic-base-item-label">课堂</div>
<div class="statistic-base-item-content">
<span><%= @courses_count %></span>个
</div>
</div>
<div class="col-4 col-md-2 statistic-base-item">
<div class="statistic-base-item-label">共建实训</div>
<div class="statistic-base-item-content">
<span><%= @shixuns_count %></span>个
</div>
</div>
<div class="col-4 col-md-2 statistic-base-item">
<div class="statistic-base-item-label">实习报告</div>
<div class="statistic-base-item-content shixun-report-count">
加载中...
</div>
</div>
<div class="col-4 col-md-2 statistic-base-item">
<div class="statistic-base-item-label">学员实战时间</div>
<div class="statistic-base-item-content shixun-time">
加载中...
</div>
</div>
</div>
</div>
<div class="statistic-container my-4 statistic-course">
<div class="row">
<div class="col-12">
<div class="statistic-label">课堂</div>
<div class="statistic-table">
<table class="table text-center course-table">
<thead class="thead-light">
<tr>
<th class="text-left">名称</th>
<th class="text-left">管理教师</th>
<th>评测次数</th>
<th>学生</th>
<th>实训作业</th>
<th>资源</th>
<th>帖子</th>
<th>其它任务</th>
<th>状态</th>
<th>时间</th>
</tr>
</thead>
<tbody>
<tr><td colspan="100">加载中...</td></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="my-4 mx-0 row">
<div class="col-12 col-md-7 statistic-container statistic-teacher-rank">
<div class="statistic-label">教师排名</div>
<div class="statistic-table">
<table class="table text-center teacher-rank-table">
<thead class="thead-light">
<tr>
<th>排名</th>
<th>姓名</th>
<th>管理课堂</th>
<th width="15%">已发布实训作业</th>
<th width="15%">未发布实训作业</th>
<th>学生数</th>
<th>完成率</th>
<th>发布实训</th>
</tr>
</thead>
<tbody>
<tr><td colspan="100">加载中...</td></tr>
</tbody>
</table>
</div>
</div>
<div class="col-12 col-md-5 statistic-container">
<div class="statistic-label">在线实训情况</div>
<div class="statistic-chart">
<div class="shixun-chart-loading">加载中...</div>
<div class="shixun-chart-empty" style="display: none;">无数据</div>
<div class="shixun-chart" id="shixun-chart"></div>
</div>
</div>
</div>
<div class="my-4 mx-0 row">
<div class="col-12 col-md-7 statistic-container statistic-student-rank">
<div class="statistic-label">学生排名</div>
<div class="statistic-table">
<table class="table text-center student-rank-table">
<thead class="thead-light">
<tr>
<th>排名</th>
<th>姓名</th>
<th>学号</th>
<th>完成实训</th>
<th>在学实训</th>
<th>金币</th>
<th>经验值</th>
</tr>
</thead>
<tbody>
<tr><td colspan="100">加载中...</td></tr>
</tbody>
</table>
</div>
</div>
<div class="col-12 col-md-5 statistic-container">
<div class="statistic-label">最热评测</div>
<div class="statistic-chart">
<div class="hot-chart-loading">加载中...</div>
<div class="hot-chart-empty" style="display: none;">无数据</div>
<div class="hot-chart" id="hot-chart"></div>
</div>
</div>
</div>
</div>

@ -0,0 +1 @@
$('.statistic-student-rank table.student-rank-table tbody').html("<%= j(render :partial => 'colleges/student_rank') %>")

@ -0,0 +1 @@
$('.statistic-teacher-rank table.teacher-rank-table tbody').html("<%= j(render :partial => 'colleges/teacher_rank') %>")

@ -0,0 +1,18 @@
json.question do
json.id question.id
json.question_number question.question_number
json.question_title question.question_title
json.question_type question.question_type
json.is_necessary question.is_necessary
if question.question_type == 2
json.max_choices question.max_choices
json.min_choices question.min_choices
end
json.answers do
json.array! answers do | a|
json.answer_id a.id
json.answer_position a.choice_position
json.answer_text a.choice_text.nil? ? "other_choices" : a.choice_text ##
end
end
end

@ -1,16 +1,36 @@
json.exercise do
json.extract! @bank, :id, :name, :description, :is_public
end
if @bank.container_type == "Exercise"
json.exercise do
json.extract! @bank, :id, :name, :description, :is_public
end
json.partial! "exercises/exercise_scores"
json.exercise_questions do
json.array! @exercise_questions do |q|
json.partial! "exercise_bank_questions/exercise_bank_questions",
question: q,
choices:q.exercise_bank_choices,
shixun_challenges: q.exercise_bank_shixun_challenges,
ques_position:nil,
edit_type:nil
end
end
else
json.poll do
json.extract! @bank, :id, :name, :description, :is_public
end
json.partial! "exercises/exercise_scores"
json.question_types do
json.q_counts @poll_questions_count
json.q_singles @poll_question_singles
json.q_doubles @poll_question_doubles
json.q_mains @poll_question_mains
end
json.exercise_questions do
json.array! @exercise_questions do |q|
json.partial! "exercise_bank_questions/exercise_bank_questions",
question: q,
choices:q.exercise_bank_choices,
shixun_challenges: q.exercise_bank_shixun_challenges,
ques_position:nil,
edit_type:nil
json.questions do
json.array! @exercise_questions do | question|
json.partial! "exercise_banks/poll_questions", question: question, answers: question.exercise_bank_choices
end
end
end

@ -31,6 +31,11 @@ else
json.question_status @question_status
end
exercise_type = 3
if @t_user_exercise_status == 3 && @exercise.answer_open
exercise_type = 4
end
json.partial! "exercises/exercise_scores"
json.exercise_questions do
@ -57,7 +62,7 @@ json.exercise_questions do
shixun_challenges: question.exercise_shixun_challenges,
user_answer: question_info[:answered_content],
choices:question.exercise_choices,
exercise_type:3,
exercise_type:exercise_type,
shixun_type:question_info[:shixun_type],
ques_position: q[:ques_number],
edit_type:nil

@ -4,6 +4,7 @@
<title>EduCoder后台管理</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel='shortcut icon' type='image/x-icon' href='/favicon.ico' />
<%= csrf_meta_tags %>
<%= csp_meta_tag %>

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<title>EduCoder</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel='shortcut icon' type='image/x-icon' href='/favicon.ico' />
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'college', media: 'all','data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'college', 'data-turbolinks-track': 'reload' %>
</head>
<% body_class = [params[:controller].gsub(/\//, '-').gsub('_', '-'), params[:action], 'page'].join('-') %>
<body class="<%= body_class %>">
<%= render 'colleges/shared/navbar' %>
<!-- Page Content -->
<div class="college-body-container">
<%= yield %>
</div>
</body>
</html>

@ -14,7 +14,7 @@ if @poll_questions_count > 0
json.array! @poll_questions do |question|
json.partial! "polls/commit_answers_result", question: question,
answers:question.poll_answers,
question_votes:question.poll_votes #问题的全部答案
question_votes:question.poll_votes.where(user_id:@poll_commit_ids) #问题的全部答案
end
end
else

@ -0,0 +1,2 @@
json.content @content
json.path @path

@ -12,5 +12,5 @@ Rails.application.config.assets.paths << Rails.root.join('vendor/assets')
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in the app/assets
# folder are already added.
Rails.application.config.assets.precompile += %w( admin.js admin.css )
Rails.application.config.assets.precompile += %w( admin.js admin.css college.js college.css )

@ -663,6 +663,8 @@ Rails.application.routes.draw do
end
end
resources :poll_bank_questions
resources :attachments
resources :schools do
@ -848,6 +850,19 @@ Rails.application.routes.draw do
end
end
resources :colleges, only: [] do
member do
get :statistics
get :course_statistics
get :student_shixun
get :shixun_time
get :shixun_report_count
get :teachers
get :shixun_chart_data
get :student_hot_evaluations
end
end
#git 认证回调
match 'gitauth/*url', to: 'gits#auth', via: :all

File diff suppressed because one or more lines are too long

@ -37830,25 +37830,24 @@ $(document).on('turbolinks:load', function() {
})
;
$(document).on('turbolinks:load', function() {
if ($('body.admins-library-applies-index-page').length > 0) {
var $searchFrom = $('.library-applies-list-form');
$searchFrom.find('select[name="status"]').val('pending');
$searchFrom.on('click', '.search-form-tab', function(){
var $link = $(this);
if ($('body.admins-library-applies-index-page').length > 0) {
var $searchFrom = $('.library-applies-list-form');
$searchFrom.find('select[name="status"]').val('pending');
$searchFrom.find('input[name="keyword"]').val('');
$searchFrom.find('select[name="status"]').val('processed');
$searchFrom.on('click', '.search-form-tab', function(){
var $link = $(this);
if($link.data('value') === 'processed'){
$searchFrom.find('.status-filter').show();
} else {
$searchFrom.find('.status-filter').hide();
$searchFrom.find('select[name="status"]').val('pending');
}
});
$searchFrom.find('input[name="keyword"]').val('');
$searchFrom.find('select[name="status"]').val('processed');
}
if($link.data('value') === 'processed'){
$searchFrom.find('.status-filter').show();
} else {
$searchFrom.find('.status-filter').hide();
$searchFrom.find('select[name="status"]').val('pending');
}
});
}
})
;
$(document).on('turbolinks:load', function() {
@ -38362,7 +38361,7 @@ $(document).on('turbolinks:load', function() {
;
$(document).on('turbolinks:load', function() {
if ($('body.admins-shixun-settings-index-page').length > 0) {
$(".shixun-settings-select").on("change", function () {
$(".shixun-settings-list-container").on("change", '.shixun-settings-select', function () {
var s_value = $(this).val();
var s_name = $(this).attr("name");
var json = {};
@ -38375,7 +38374,7 @@ $(document).on('turbolinks:load', function() {
})
});
$(".shixun-setting-form").on("change",function () {
$(".shixun-settings-list-container").on("change", '.shixun-setting-form', function () {
var s_id = $(this).attr("data-id");
var s_value = $(this).val();
var s_name = $(this).attr("name");
@ -38394,10 +38393,12 @@ $(document).on('turbolinks:load', function() {
});
$(document).on('turbolinks:load', function() {
if($('body.admins-shixuns-index-page').length > 0){
$('select#tag-choosed').select2({
placeholder: "请选择分类",
allowClear: true
placeholder: "请选择分类",
allowClear: true
});
}
});
$(document).on('turbolinks:load', function(){
$('#sidebarCollapse').on('click', function () {

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

@ -55,6 +55,9 @@ html, body {
.markdown-body p {
white-space: pre-wrap;
}
.markdown-body > p {
line-height: 25px;
}
/* https://www.educoder.net/courses/2346/group_homeworks/34405/question */
.renderAsHtml.markdown-body p {
white-space: inherit;

@ -1,4 +1,4 @@
import { bytesToSize, getUrl2 } from 'educoder';
import { bytesToSize, getUrl, getUrl2 } from 'educoder';
const $ = window.$
export function isImageExtension(fileName) {
@ -41,7 +41,7 @@ export function markdownToHTML(oldContent, selector) {
}
}
function _doDownload(options) {
$.fileDownload("/api" + options.url, {
$.fileDownload(getUrl() + "/api" + options.url, {
successCallback: options.successCallback,
failCallback: options.failCallback
});

@ -37,6 +37,14 @@ export function getUrl(path, goTest) {
}
return `${path ? path: ''}`;
}
export function getStaticUrl() {
const local = TEST_HOST;
if (isDev) {
return local
}
// todo cdn
return ''
}
export function getUrl2(path, goTest) {
const local = 'http://localhost:3000'
if (isDev) {

@ -838,7 +838,7 @@ class Fileslists extends Component{
{course_modules&&course_modules.course_modules.map((item,key)=>{
return(
filesId&&filesId===item.id?"":<li key={key} id={item.id} onClick={() => this.moveTos(0)}>{item.module_name}</li>
filesId&&filesId===item.id?"":<li key={key} id={item.id} onClick={() => this.moveTos(0)} title={item.module_name}>{item.module_name}</li>
)
})}
@ -848,7 +848,7 @@ class Fileslists extends Component{
return (!this.state.dirSearchValue || item.name.indexOf(this.state.dirSearchValue) != -1)
}).map((itm,k)=>{
return(
filesId&&filesId===itm.id?"":<li key={k} id={itm.id} onClick={() => this.moveTos(itm.id )}>{itm.name}</li>
filesId&&filesId===itm.id?"":<li key={k} id={itm.id} onClick={() => this.moveTos(itm.id )} title={itm.name}>{itm.name}</li>
)
})
})}

@ -387,7 +387,7 @@ class Boards extends Component{
boards && boards.filter((item)=> {
return item.id != bid && (!this.state.dirSearchValue || item.name.indexOf(this.state.dirSearchValue) != -1)
}).map( (item) => {
return <li onClick={() => this.moveTo(item)}>{item.name}</li>
return <li onClick={() => this.moveTo(item)} title={item.name}>{item.name}</li>
})
}
{ isAdmin &&

@ -108,7 +108,7 @@ function buildColumns(that, student_works, studentData) {
}]
if (!niPingAndIsStudent && isAdminOrStudent) {
columns.push({
width: 88,
width: isStudent ? undefined : 88,
title: '学号',
dataIndex: 'student_id',
key: 'student_id',
@ -197,7 +197,7 @@ function buildColumns(that, student_works, studentData) {
</span>
)},
}, {
width: 106,
width: 106, // isStudent ? undefined : 106 , // 匿评中 只有这几列: 序号 姓名 提交状态 更新时间 匿评评分 操作
title: '更新时间',
dataIndex: 'update_time',
key: 'update_time',

@ -56,7 +56,8 @@ class Coursesleftnav extends Component{
positiontype:undefined,
toopvisible:false,
toopvisibleindex:undefined,
sandiantypes:undefined
sandiantypes:undefined,
antIcon:false
}
}
@ -582,7 +583,9 @@ class Coursesleftnav extends Component{
}
deletenavchilds=(url,mainurl)=>{
this.setState({
antIcon:true
})
axios.delete(url).then((result)=>{
if(result.data.status===0){
@ -809,6 +812,7 @@ class Coursesleftnav extends Component{
modalSave={ModalSave}
modalCancel={this.cannerNavmoda}
loadtype={loadtype}
antIcon={this.state.antIcon}
>
</Modals>

@ -132,7 +132,7 @@ class Sendresource extends Component{
onAttachmentRemove = (file) => {
debugger
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
axios.delete(url, {
})

@ -4,14 +4,14 @@ import {Form,Checkbox,DatePicker,Button,Input,Select,Tooltip} from "antd";
import { handleDateString } from 'educoder';
import PollDetailTabForthRules from '../poll/PollDetailTabForthRules';
import '../css/members.css'
import '../css/busyWork.css'
import '../poll/pollStyle.css'
import '../css/members.css';
import '../css/busyWork.css';
import '../poll/pollStyle.css';
import moment from 'moment'
import moment from 'moment';
import locale from 'antd/lib/date-picker/locale/zh_CN';
import axios from 'axios'
import Modals from '../../modals/Modals'
import axios from 'axios';
import Modals from '../../modals/Modals';
const Search=Input.Search;
const Option=Select.Option;
@ -549,6 +549,7 @@ class Exercisesetting extends Component{
}
//取消编辑
cancelEdit=()=>{
this.getSettingInfo();
this.setState({
flagPageEdit:false
})

@ -129,7 +129,7 @@ class fillEmpty extends Component{
}
{
// 答案公开,且试卷已经截止
isAdmin &&
questionType.standard_answer &&
<div>
<p className="bor-top-greyE pt20 mt20 font-16 mb10">参考答案</p>
{ questionType.standard_answer && questionType.standard_answer.map((item,k)=>{

@ -85,9 +85,9 @@ class simpleAnswer extends Component{
</div>
}
{
isStudent && exercise.answer_open==true && exercise.exercise_status == 3 ?
isStudent && questionType.standard_answer ?
<div className="bor-top-greyE pt20 mt20 standardAnswer">
<p>参考答案</p>
<p className="mb10 font-16">参考答案</p>
{/* <li className="markdown-body answerStyle" dangerouslySetInnerHTML={{__html: markdownToHTML1(questionType.standard_answer && questionType.standard_answer[0])}}></li> */}
<MarkdownToHtml content={questionType.standard_answer && questionType.standard_answer[0]} selector={'simgle_standard2_' + (this.props.index + 1)}
className="answerStyle"

@ -778,6 +778,7 @@ class GraduationTaskssettingapp extends Component{
}
isgoback=()=>{
this.getsettings();
this.setState({
flagPageEdit: false,
})

@ -7,8 +7,7 @@ import CourseLayoutcomponent from '../common/CourseLayoutComponent'
import Titlesearchsection from '../common/titleSearch/TitleSearchSection'
import ColorCountText from '../common/titleSearch/ColorCountText'
import { WordsBtn, trigger, on, off ,downloadFile} from 'educoder'
import { WordsBtn, trigger, on, off, getUrl, downloadFile } from 'educoder'
import Modals from "../../modals/Modals";
import axios from 'axios'
import _ from 'lodash'
@ -125,12 +124,12 @@ class studentsList extends Component{
isSpin:false,
DownloadType:false,
DownloadMessageval:undefined,
donwloading:false,
}
}
/// 确认是否下载
confirmysl(url){
axios.get(url).then((response) => {
// this.props.showGlobalLoading('正在生成文件,请稍后...')
axios.get(url + 'export=true').then((response) => {
if(response === undefined){
return
}
@ -152,22 +151,27 @@ class studentsList extends Component{
})
}
}else {
this.props.showNotification(`正在下载中`);
// window.open("/api"+url, '_blank');
this.setState({ donwloading: true })
downloadFile({
url: url,
successCallback: (url) => {
console.log('successCallback')
this.setState({ donwloading: false })
},
failCallback: (responseHtml, url) => {
console.log('failCallback')
this.setState({ donwloading: false })
}
})
// this.props.showNotification(`正在下载中`);
// window.open("/api"+url, '_blank');
this.props.slowDownload(url)
// getUrl() + "/api"+
// const fileUrl = url;
// this.props.slowDownload(fileUrl)
// return;
// downloadFile({
// url: fileUrl,
// successCallback: (url) => {
// console.log('successCallback')
// },
// failCallback: (responseHtml, url) => {
// console.log('failCallback')
// }
// })
// window.open(fileUrl, "_self");// , '_blank'
}
}).catch((error) => {
console.log(error)
@ -609,22 +613,20 @@ class studentsList extends Component{
`}</style>
{ isAdmin &&
<Spin spinning={this.state.donwloading} style={{ }}>
<li className="li_line drop_down fr color-blue font-16">
导出<i className="iconfont icon-xiajiantou font-12 ml2"></i>
<ul className="drop_down_menu" style={{"right": "-20px", "left": "unset", "height": "auto"}}>
<li onClick={(i) => this.confirmysl(exportUrltwo)}><a
<li><a
onClick={(i) => this.confirmysl(exportUrltwo)}>课堂信息</a>
</li>
<li onClick={(i) => this.confirmysl(exportUrlthree)}><a
<li><a
onClick={(i) => this.confirmysl(exportUrlthree)}>活跃度</a>
</li>
<li onClick={(i) => this.confirmysl(exportUrl)}><a
<li><a
onClick={(i) => this.confirmysl(exportUrl)}>总成绩</a>
</li>
</ul>
</li>
</Spin>
}
{/*<WordsBtn style="blue" className="" onClick={(url)=>this.confirmysl(exportUrl)} >导出成绩</WordsBtn>*/}
{/* */}
@ -658,7 +660,7 @@ class studentsList extends Component{
return item.id != course_group_id && (!this.state.groupSearchValue || item.name.indexOf(this.state.groupSearchValue) != -1)
}).map( item => {
return (
<li key={item.id} onClick={() => this.moveToGroup(item)}>{item.name}</li>
<li key={item.id} onClick={() => this.moveToGroup(item)} title={item.name}>{item.name}</li>
)
}) }
{ isAdmin &&

@ -126,7 +126,9 @@ function buildColumns(that) {
sortOrder: sortedInfo.columnKey === 'graduation_group' && sortedInfo.order,
render: text => (
<span>
<span className="overflowHidden1" style={{ maxWidth: '160px'}}
title={`${text && text.length > 10 ? text : ''}`}
>
{text}
</span>
),

@ -679,7 +679,7 @@ class CoursesNew extends Component {
}
.yslzxueshiskmc .ant-input-group{
width: 720px !important;
width: 704px !important;
}
.yslzxueshisy span .ant-input-group-addon{
width: 65px !important;
@ -690,10 +690,10 @@ class CoursesNew extends Component {
background-color: #fafafa!important;
}
.yslzxueshiskmc .ant-input-group-wrapper{
width: 720px !important;
width: 704px !important;
}
.yslzxueshiskmcs .ant-input-group-wrapper{
width: 720px !important;
width: 704px !important;
}
`
}</style>

@ -1,14 +1,13 @@
import React,{ Component } from "react";
import {Checkbox,Radio} from "antd";
import '../css/members.css'
import '../css/busyWork.css'
import './pollStyle.css'
import PollDetailTabThirdInfo from './PollDetailTabThirdInfo'
import axios from 'axios';
const map={1:"单选题",2:"多选题",3:"主观题",4:"主观题"}
class PollDetailTabThird extends Component{
constructor(props){
super(props);
@ -35,102 +34,8 @@ class PollDetailTabThird extends Component{
render(){
let {pollDetail}=this.state;
return(
<div className="edu-back-white">
{
pollDetail && pollDetail.poll.polls_description &&
<p style={{whiteSpace:"pre-wrap"}} className="color-grey-9 padding20-30 edu-back-white">{ pollDetail.poll.polls_description }</p>
}
<p className="padding20-30 clearfix edu-txt-left" style={{background:'#FAFAFA'}}>
{ pollDetail && pollDetail.question_types.q_counts===0 ? "" :
<span className="color-grey-3">
{
pollDetail && pollDetail.question_types.q_counts > 0 &&
<span>合计{pollDetail.question_types.q_counts}</span>
}
{
pollDetail && pollDetail.question_types.q_singles > 0 &&
<span className="mr15 color-grey-9">单选题{pollDetail.question_types.q_singles}</span>
}
{
pollDetail && pollDetail.question_types.q_doubles > 0 &&
<span className="mr15 color-grey-9">多选题{pollDetail.question_types.q_doubles}</span>
}
{
pollDetail && pollDetail.question_types.q_mains > 0 &&
<span className="color-grey-9">主观题{pollDetail.question_types.q_mains}</span>
}
</span>
}
</p>
{
pollDetail && pollDetail.questions.map((item,key)=>{
return(
<div className="previewList">
<p className="pl30 pr30 pt30 pb15 font-16 clearfix">
<span className="color-blue mr8 fl">{item.question.question_number}{map[item.question.question_type]}</span>
{ item.question.is_necessary==1 ? <span className="mustAnswer fl ml10 mr10">必答</span>:<span className="mustAnswer fl ml10 mr10"></span> }
{ item.question.question_type == 2 && item.question.min_choices && item.question.max_choices ?
<span className="color-grey-9 font-14 fl mt2">
{
item.question.min_choices == item.question.max_choices ? "可选"+item.question.max_choices+"项" :
"可选"+item.question.min_choices+"-"+item.question.max_choices+"项"
}
</span>:""
}
</p>
<li className="pl30 pr30 pb15">{item.question.question_title}</li>
{
// 单选题
item.question.question_type==1 &&
<Radio.Group className="answerList" disabled>
{
item.question.answers.map((index,k)=>{
return(
<li className="df">
<Radio className="fl" value={index.answer_id}></Radio>
<span className={index.answer_text=="其他"?"break-word":"break-word flex1"}>{index.answer_text}</span>
{
index.answer_text=="其他" ? <p className="textLine"></p>:""
}
</li>
)
})
}
</Radio.Group>
}
{
// 多选题
item.question.question_type==2 &&
<Checkbox.Group className="answerList" disabled>
{
item.question.answers.map((index,k)=>{
return(
<li className="df" key={k}>
<Checkbox className="fl mr8" value={index.answer_id} key={index.answer_id}></Checkbox>
<span className={index.answer_text=="其他"?"break-word":"break-word flex1"}>{index.answer_text}</span>
{
index.answer_text=="其他" ? <p className="textLine"></p>:""
}
</li>
)
})
}
</Checkbox.Group>
}
{
// 主观题
item.question.question_type == 3 &&
<div className="mt10 pl30 pr30 pb20">
<textarea placeholder="在此填入答案" readOnly className="winput-100-130"></textarea>
</div>
}
</div>
)
})
}
<div>
<PollDetailTabThirdInfo {...this.props} {...this.state} pollDetail = {pollDetail}></PollDetailTabThirdInfo>
</div>
)
}

@ -0,0 +1,117 @@
import React,{ Component } from "react";
import {Checkbox,Radio} from "antd";
import '../css/members.css'
import '../css/busyWork.css'
import './pollStyle.css'
const map={1:"单选题",2:"多选题",3:"主观题",4:"主观题"}
class PollDetailTabThirdInfo extends Component{
constructor(props){
super(props);
}
render(){
let { pollDetail }=this.props;
return(
<div className="edu-back-white">
{
pollDetail && pollDetail.poll.polls_description &&
<p style={{whiteSpace:"pre-wrap"}} className="color-grey-3 padding20-30">{ pollDetail.poll.polls_description }</p>
}
<p className="padding20-30 clearfix edu-txt-left" style={{background:"#fafafa"}}>
{ pollDetail && pollDetail.question_types.q_counts===0 ? "" :
<span className="color-grey-3">
{
pollDetail && pollDetail.question_types.q_counts > 0 &&
<span>合计{pollDetail.question_types.q_counts}</span>
}
{
pollDetail && pollDetail.question_types.q_singles > 0 &&
<span className="mr15 color-grey-9">单选题{pollDetail.question_types.q_singles}</span>
}
{
pollDetail && pollDetail.question_types.q_doubles > 0 &&
<span className="mr15 color-grey-9">多选题{pollDetail.question_types.q_doubles}</span>
}
{
pollDetail && pollDetail.question_types.q_mains > 0 &&
<span className="color-grey-9">主观题{pollDetail.question_types.q_mains}</span>
}
</span>
}
</p>
{
pollDetail && pollDetail.questions.map((item,key)=>{
return(
<div className="previewList">
<p className="pl30 pr30 pt30 pb15 font-16 clearfix">
<span className="color-blue mr8 fl">{item.question.question_number}{map[item.question.question_type]}</span>
{ item.question.is_necessary==1 ? <span className="mustAnswer fl ml10 mr10">必答</span>:<span className="mustAnswer fl ml10 mr10"></span> }
{ item.question.question_type == 2 && item.question.min_choices != undefined && item.question.min_choices != null && item.question.max_choices != undefined && item.question.max_choices != null ?
<span className="color-grey-9 font-14 fl mt2">
{
item.question.min_choices == item.question.max_choices ? "可选"+item.question.max_choices+"项" :
"可选"+item.question.min_choices+"-"+item.question.max_choices+"项"
}
</span>:""
}
</p>
<li className="pl30 pr30 pb15">{item.question.question_title}</li>
{
// 单选题
item.question.question_type==1 &&
<Radio.Group className="answerList" disabled>
{
item.question.answers.map((index,k)=>{
return(
<li className="df">
<Radio className="fl" value={index.answer_id}></Radio>
<span className={index.answer_text=="其他"?"break-word":"break-word flex1"}>{index.answer_text}</span>
{
index.answer_text=="其他" ? <p className="textLine"></p>:""
}
</li>
)
})
}
</Radio.Group>
}
{
// 多选题
item.question.question_type==2 &&
<Checkbox.Group className="answerList" disabled>
{
item.question.answers.map((index,k)=>{
return(
<li className="df" key={k}>
<Checkbox className="fl mr8" value={index.answer_id} key={index.answer_id}></Checkbox>
<span className={index.answer_text=="其他"?"break-word":"break-word flex1"}>{index.answer_text}</span>
{
index.answer_text=="其他" ? <p className="textLine"></p>:""
}
</li>
)
})
}
</Checkbox.Group>
}
{
// 主观题
item.question.question_type == 3 &&
<div className="pl30 pr30 pb20">
<textarea placeholder="在此填入答案" readOnly className="winput-100-130"></textarea>
</div>
}
</div>
)
})
}
</div>
)
}
}
export default PollDetailTabThirdInfo

@ -54,7 +54,7 @@ class PollListItem extends Component{
}
</p>
<p className="color-grey-9 clearfix">
{ item.author && <span className="mr20 fl">{item.author}</span> }
{ item.author && <span className="mr20 fl mt3">{item.author}</span> }
{
item.polls_status !=1 &&
<span className="fl mt3">

@ -2123,7 +2123,7 @@ class PollNew extends Component {
//最小值
HandleGradationGroupChangee = (value, index, max, length) => {
// debugger
var minbool = false;
var maxbool = false;
let arr = this.state.adddom;
@ -2146,8 +2146,19 @@ class PollNew extends Component {
} else {
for (var i = 0; i < arr.length; i++) {
if (index === i) {
arr[i].question.min_choices = parseInt(value);
arr[i].question.max_choices = length;
try {
if(parseInt(value)===0){
arr[i].question.min_choices = 2;
arr[i].question.max_choices = length;
}else{
arr[i].question.min_choices = parseInt(value);
arr[i].question.max_choices = length;
}
}catch (e) {
arr[i].question.min_choices = 2;
arr[i].question.max_choices = length;
}
break;
}
}
this.setState({

@ -54,7 +54,8 @@ class ShixunHomework extends Component{
course_groupslist:[],
checkedtype:false,
checkBoxValues:[],
isSpin:false
isSpin:false,
antIcon:false
}
}
updateNavSuccess=()=>{
@ -660,10 +661,11 @@ class ShixunHomework extends Component{
savedelete=()=>{
this.setState({
antIcon:true
})
let {Coursename,page,order,checkBoxValues,datas}=this.state;
let category_id=this.props.match.params.category_id;
const cid = this.props.match.params.coursesId
const url = `/courses/`+cid+`/homework_commons/multi_destroy.json`;
axios.post(url, {
@ -691,11 +693,17 @@ class ShixunHomework extends Component{
Loadtype:false,
checkBoxValues:[],
checkedtype:false,
antIcon:false
})
this.props.showNotification(response.data.message)
this.homeworkupdatalist(Coursename,page,order);
this.props.updataleftNavfun()
}
}else{
this.setState({
antIcon:false
})
this.props.showNotification(response.data.message)
}
})
.catch(function (error) {
console.log(error);
@ -923,6 +931,7 @@ class ShixunHomework extends Component{
course_modules,
shixunpath,
order,
antIcon,
}=this.state;
let main_id=this.props.match.params.main_id;
@ -938,6 +947,7 @@ class ShixunHomework extends Component{
modalSave={this.state.ModalSave}
modalsBottomval={this.state.ModalsBottomval}
loadtype={this.state.Loadtype}
antIcon={this.state.antIcon}
/>:""}
{/*立即发布*/}
{visible===true?<HomeworkModal
@ -1090,7 +1100,7 @@ class ShixunHomework extends Component{
{course_modules&&course_modules.main_category.map((item,key)=>{
return(
datas&&datas.category_id===null?"":<li key={key} id={item.main_category_id} onClick={() => this.moveTos(item.main_category_id)}>{item.main_category_name}</li>
datas&&datas.category_id===null?"":<li key={key} id={item.main_category_id} onClick={() => this.moveTos(item.main_category_id)} title={item.main_category_name}>{item.main_category_name}</li>
)
})}
@ -1099,10 +1109,10 @@ class ShixunHomework extends Component{
return (!this.state.dirSearchValue || item.category_name.indexOf(this.state.dirSearchValue) != -1)
}).map( (item,key) => {
if(datas&&datas.category_id!=null&&datas&&datas.category_id===item.category_id===false){
return <li key={key} id={item.category_id} onClick={() => this.moveTos(item.category_id )}>{item.category_name}</li>
return <li key={key} id={item.category_id} onClick={() => this.moveTos(item.category_id )} title={item.category_name}>{item.category_name}</li>
}
if(datas&&datas.category_id===null){
return <li key={key} id={item.category_id} onClick={() => this.moveTos(item.category_id )}>{item.category_name}</li>
return <li key={key} id={item.category_id} onClick={() => this.moveTos(item.category_id )} title={item.category_name}>{item.category_name}</li>
}
})}

@ -57,6 +57,7 @@ class MemoDetailMDEditor extends Component {
errorMsg: ''
})
})
commentMDEditor.cm.focus()
}, {
watch: false,
dialogLockScreen: false,
@ -124,7 +125,7 @@ class MemoDetailMDEditor extends Component {
this.initMDEditor()
} else {
setTimeout(() => {
this.commentMDEditor && this.commentMDEditor.focus()
this.commentMDEditor && this.commentMDEditor.cm.focus()
}, 10)
}
}

@ -1,158 +1,100 @@
import React,{ Component } from "react";
import { Modal,Radio,Input,Tooltip,Checkbox,Select, Row,Col } from "antd";
import axios from 'axios';
import { SnackbarHOC } from 'educoder';
import Modals from './Modals';
const { Search } = Input;
class SendTopics extends Component{
constructor(props){
super(props);
this.state={
sentShixunPath:false,
sendToCourseList:undefined,
openSearch:false,
sendToCourseId:undefined,
sendToShixunArray:[],
Modalstype:false,
cardsModalcancel:this.cardsModalcancel,
cardsModalsave:this.cardsModalsave,
modalsTopval:'',
Modalsbottomval:'',
courseurl:''
courses:[],
search:null,
Radiolist:undefined,
showcheck:false
}
}
//发送至
SentToLesson =() =>{
let id=this.props.detailInfoList.id;
let url="/paths/"+id+"/choose_course.json";
axios.get(url).then((result)=>{
if(result.status==200){
if (result.data.status === 403||result.data.status === 402||result.data.status === 407||result.data.status === 408) {
}else{
this.setState({
sendToCourseList:result.data,
sentShixunPath:true
})
}
componentDidMount(){
let{search}=this.state;
this.onupdatalist(search)
}
onupdatalist=(search)=>{
let url="/question_banks/my_courses.json";
axios.get(url,{params:{
search
}
}).then((result)=>{
this.setState({
courses:result.data.courses
})
}).catch((error)=>{
console.log(error);
})
// this.setState({
// sentShixunPath:true
// })
}
//隐藏发送至弹框
hideSenttothevalue =()=>{
this.setState({
sentShixunPath:false,
sendToShixunArray:[],
sendToCourseId:undefined,
})
this.props.topicscancelmodel()
}
//打开课堂列表下拉框
openList=()=>{
this.setState({
openSearch:true
})
}
//关闭课堂列表下拉框
closeList=()=>{
this.setState({
openSearch:false
})
}
// 选择课堂获取选中的Id
selectCloseList=(e)=>{
this.setState({
openSearch:false,
sendToCourseId:e
})
}
}
//选择checkbox
changeCheckBoxs=(list)=>{
this.setState({
sendToShixunArray:list,
// shixunNum:list.length
})
}
//确认提交
submitInfo=()=>{
let {sendToCourseId,sendToShixunArray}=this.state;
if(sendToCourseId===undefined){
this.props.showSnackbar("您还未选择发送的课堂");
}else if(parseInt(sendToShixunArray.length)==0){
this.props.showSnackbar("您还未选择实训");
}else{
let id=this.props.detailInfoList.id;
let url="/paths/"+id+"/send_to_course.json";
axios.post(url,{
shixun_ids:sendToShixunArray,
course_id:sendToCourseId
}).then((result)=>{
if(result.data.status===1){
this.setState({
Modalstype:true,
sentShixunPath:false,
Modalstopval:result.data.message,
courseurl:result.data.url,
sendToShixunArray:[],
sendToCourseId:undefined,
})
}
}).catch((error)=>{
console.log(error);
})
}
}
componentDidMount(){
// let id=this.props.detailInfoList.id;
// let url="/paths/"+id+"/choose_course.json";
// axios.get(url).then((result)=>{
// if(result.status==200){
// this.setState({
// sendToCourseList:result.data
// })
// }
// }).catch((error)=>{
// console.log(error);
// })
}
onSearchChange=(e)=>{
this.setState({
search:e.target.value
})
// this.onupdatalist(e.target.value)
}
onSearch=(search)=>{
this.onupdatalist(search)
}
onChange=(e)=>{
this.setState({
Radiolist:e.target.value
})
}
submitInfo=()=>{
let{Radiolist}=this.state;
let url=`/question_banks/send_to_course.json`;
let object_id=this.props.checkBoxValues;
let object_type=this.props.category;
if(Radiolist===undefined){
this.setState({
showcheck:true
})
}else{
axios.post(url,{
object_id: object_id,
object_type:object_type,
course_id:Radiolist
}
).then((result)=>{
if(result.data.status===0){
this.props.updataslist()
this.props.topicscancelmodel()
this.props.showNotification(result.data.message)
}else{
this.props.showNotification(result.data.message)
}
}).catch((error)=>{
console.log(error)
})
}
cardsModalcancel=()=>{
this.setState({
Modalstype:false,
})
}
cardsModalsave=()=>{
let {courseurl}=this.state;
window.location.href =courseurl;
}
}
render(){
let{courses,Radiolist,showcheck}= this.state;
const radioStyle = {
display: 'block',
height: '30px',
lineHeight: '30px',
};
render(){
let{sentShixunPath,sendToCourseList,Modalstype,Modalstopval,Modalsbottomval,cardsModalcancel,cardsModalsave}= this.state;
return(
<div>
<Modals
modalsType={Modalstype}
modalsTopval={Modalstopval}
modalsBottomval={Modalsbottomval}
modalCancel={cardsModalcancel}
modalSave={cardsModalsave}
>
</Modals>
<style>
{
`
@ -164,6 +106,7 @@ class SendTopics extends Component{
}
.over221{
height:221px;
overflow-y: auto;
}
`
}
@ -192,26 +135,25 @@ class SendTopics extends Component{
></Search>
</div>
<div className="edu-back-skyblue pl15 pr15 clearfix over221 pt5">
<Radio.Group onChange={this.onChange} value={Radiolist}>
{
courses && courses.map((item,key)=>{
return(
<div className="mt5" key={key}>
<Radio style={radioStyle} value={item.course_id} key={item.course_id}>
{item.course_name}
</Radio>
</div>
)
})
}
</Radio.Group>
{
sendToCourseList && sendToCourseList.stages.map((item,key)=>{
return(
item.shixuns.map((items,keys)=>{
return(
<div className="mt5" key={keys}>
<Checkbox name={key} value={items.shixun_id} key={items.shixun_id}>{items.shixun_name}</Checkbox>
</div>
)
})
)
})
}
</div>
{showcheck===true?<div className={"color-red mt10"}>请先选择课堂</div>:""}
<div className="mt20 clearfix edu-txt-center">
<a onClick={this.hideSenttothevalue} className="pop_close task-btn mr30">取消</a>
<a className="task-btn task-btn-orange" onClick={this.submitInfo}>确定</a>
<a onClick={()=>this.props.topicscancelmodel()} className="pop_close task-btn mr30">取消</a>
<a className="task-btn task-btn-orange" onClick={()=>this.submitInfo()}>确定</a>
</div>
</div>
</Modal>
@ -220,4 +162,4 @@ class SendTopics extends Component{
}
}
export default SnackbarHOC()(SendTopics);
export default SendTopics;

@ -5,9 +5,9 @@ import PropTypes from 'prop-types';
import NewHeader from './NewHeader'
import NewFooter from './NewFooter'
import SiderBar from './SiderBar'
import { getUrl } from 'educoder'
import { getUrl, downloadFile } from 'educoder'
import axios from 'axios';
import { Spin } from 'antd'
import './TPMIndex.css'
import LoginDialog from '../login/LoginDialog';
import AccountProfile from '../user/AccountProfile';
@ -79,7 +79,9 @@ export function TPMIndexHOC(WrappedComponent) {
coursedata: {},
isRender: false,
AccountProfiletype: false
AccountProfiletype: false,
globalLoading: false
}
}
@ -133,7 +135,18 @@ export function TPMIndexHOC(WrappedComponent) {
})
}
keyupListener = (e) => {
if (e.key === "Escape") {
this.setState({ globalLoading: false })
}
}
componentWillUnmount() {
window.removeEventListener('keyup', this.keyupListener)
}
componentDidMount() {
window.addEventListener('keyup', this.keyupListener)
if(this.props.match.path==="/"){
document.title="创新源于实践";
}else if(this.props.match.path==="/403"){
@ -364,6 +377,29 @@ export function TPMIndexHOC(WrappedComponent) {
DownloadOpenPdf=(type,url)=>{
type===true?window.open(url):window.location.href=url;
}
slowDownload = (url, tip) => {
this._gLoadingTip = tip || '正在生成文件,请稍后...';
this.setState({ globalLoading: true })
const fileUrl = url;
downloadFile({
url: fileUrl,
successCallback: (url) => {
this.setState({ globalLoading: false })
console.log('successCallback')
},
failCallback: (responseHtml, url) => {
this.setState({ globalLoading: false })
console.log('failCallback')
}
})
}
showGlobalLoading = (tip) => {
this._gLoadingTip = tip || '加载中,请稍后...';
this.setState({ globalLoading: true })
}
hideGlobalLoading = () => {
this.setState({ globalLoading: false })
}
render() {
let{Headertop,Footerdown, isRender, AccountProfiletype}=this.state;
const common = {
@ -387,10 +423,15 @@ export function TPMIndexHOC(WrappedComponent) {
ShowOnlinePdf:(url)=>this.ShowOnlinePdf(url),
DownloadFileA:(title,url)=>this.DownloadFileA(title,url),
DownloadOpenPdf:(type,url)=>this.DownloadOpenPdf(type,url)
DownloadOpenPdf:(type,url)=>this.DownloadOpenPdf(type,url),
slowDownload: this.slowDownload,
showGlobalLoading: this.showGlobalLoading,
hideGlobalLoading: this.hideGlobalLoading,
}
return (
<div>
<div className="indexHOC">
{isRender===true ? <LoginDialog
Modifyloginvalue={()=>this.hideLoginDialog()}
{...this.props}
@ -423,8 +464,28 @@ export function TPMIndexHOC(WrappedComponent) {
-moz-box-shadow: 0px 0px 12px rgba(0,0,0,0.1);
box-shadow: 0px 0px 12px rgba(0,0,0,0.1);
}
.globalSpin {
max-height: 700px !important;
}
.indexHOC > .ant-spin-nested-loading {
background: #000;
}
.globalSpin .ant-spin-text {
text-shadow: none !important;
color: #fff;
}
.globalSpin .ant-spin-dot-item {
background-color: #fff;
}
`
}</style>
<Spin spinning={this.state.globalLoading} delay={0} className="globalSpin"
size="large"
tip= {this._gLoadingTip || "加载中..."}
>
<NewHeader {...this.state} {...this.props}></NewHeader>
<div className="newContainer newContainers">
<WrappedComponent initCommonState={(user)=>this.initCommonState(user)}
@ -440,6 +501,7 @@ export function TPMIndexHOC(WrappedComponent) {
<NewFooter
Footerdown={Footerdown}
/>
</Spin>
</div>
);
}

@ -4,139 +4,43 @@ 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 axios from 'axios';
import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor';
import {getUrl} from 'educoder';
let origin = getUrl();
let path = getUrl("/editormd/lib/")
const $ = window.$;
let timeout;
let currentValue;
const Option = Select.Option;
const RadioGroup = Radio.Group;
function create_editorMD(id, width, high, placeholder, imageUrl, callback) {
var editorName = window.editormd(id, {
width: width,
height: high,
path: path, // "/editormd/lib/"
syncScrolling: "single",
tex: true,
tocm: true,
emoji: true,
taskList: true,
codeFold: true,
searchReplace: true,
htmlDecode: "style,script,iframe",
sequenceDiagram: true,
autoFocus: false,
toolbarIcons: function () {
// Or return editormd.toolbarModes[name]; // full, simple, mini
// Using "||" set icons align right.
return ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "|", "testIcon", "testIcon1", '|', "image", "table", '|', "watch", "clear"]
},
toolbarCustomIcons: {
testIcon: "<a type=\"inline\" class=\"latex\" ><div class='zbg'></div></a>",
testIcon1: "<a type=\"latex\" class=\"latex\" ><div class='zbg_latex'></div></a>"
},
//这个配置在simple.html中并没有但是为了能够提交表单使用这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中方便post提交表单。
saveHTMLToTextarea: true,
// 用于增加自定义工具栏的功能可以直接插入HTML标签不使用默认的元素创建图标
dialogMaskOpacity: 0.6,
placeholder: placeholder,
imageUpload: true,
imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp", "JPG", "JPEG", "GIF", "PNG", "BMP", "WEBP"],
imageUploadURL: imageUrl,//url
onload: function () {
// this.previewing();
$("#" + id + " [type=\"latex\"]").bind("click", function () {
editorName.cm.replaceSelection("```latex");
editorName.cm.replaceSelection("\n");
editorName.cm.replaceSelection("\n");
editorName.cm.replaceSelection("```");
var __Cursor = editorName.cm.getDoc().getCursor();
editorName.cm.setCursor(__Cursor.line - 1, 0);
});
$("#" + id + " [type=\"inline\"]").bind("click", function () {
editorName.cm.replaceSelection("$$$$");
var __Cursor = editorName.cm.getDoc().getCursor();
editorName.cm.setCursor(__Cursor.line, __Cursor.ch - 2);
editorName.cm.focus();
});
$("[type=\"inline\"]").attr("title", "行内公式");
$("[type=\"latex\"]").attr("title", "多行公式");
window.md_elocalStorage(editorName, `UpdatepropaedeMDs_${id}`, "UpdatepropaedeMDs");
callback && callback()
}
});
return editorName;
}
export default class TPMUpdatepropaede extends Component {
constructor(props) {
super(props)
this.neweditanswerRef=React.createRef();
this.state = {
shixunId:undefined
shixunId:undefined,
}
}
updatepropaedeMD(initValue, id) {
this.contentChanged = false;
const placeholder = "";
// amp;
// 编辑时要传memoId
const imageUrl = `/api/attachments.json`;
// 创建editorMd
const Updatepropaede_editormd = create_editorMD(id, '100%', 400, placeholder, imageUrl, () => {
setTimeout(() => {
Updatepropaede_editormd.resize()
Updatepropaede_editormd.cm && Updatepropaede_editormd.cm.refresh()
}, 500)
if (initValue != undefined) {
Updatepropaede_editormd.setValue(initValue)
}
Updatepropaede_editormd.cm.on("change", (_cm, changeObj) => {
console.log('....contentChanged')
this.contentChanged = true;
})
});
this.Updatepropaede_editormd = Updatepropaede_editormd;
window.Updatepropaede_editormd = Updatepropaede_editormd;
}
componentDidMount() {
let id = this.props.match.params.shixunId;
let url="/shixuns/"+id+"/propaedeutics.json";
axios.get(url).then((response) => {
console.log(response)
if (response.data.status === 403||response.data.status === 401||response.data.status === 500) {
}else{
this.setState({
shixunId:id
shixunId:id,
})
if(response.data.content[0]!=null){
this.updatepropaedeMD(response.data.content, "UpdatepropaedeMD");
this.setState({
editanswersRefval:response.data.content,
})
this.neweditanswerRef.current.setValue(response.data.content)
}else{
this.updatepropaedeMD("", "UpdatepropaedeMD");
this.setState({
editanswersRefval:"",
})
this.neweditanswerRef.current.setValue('')
}
}
}).catch((error) => {
@ -148,7 +52,7 @@ export default class TPMUpdatepropaede extends Component {
updatepropaedeuticsvalue=()=>{
let id = this.props.match.params.shixunId;
let url="/shixuns/"+id+"/update_propaedeutics.json";
const update_propaedeuticsvalue = this.Updatepropaede_editormd.getValue();
const update_propaedeuticsvalue = this.editanswersRef.current.getValue().trim();
axios.post(url,{
content:update_propaedeuticsvalue
}
@ -163,10 +67,7 @@ export default class TPMUpdatepropaede extends Component {
});
}
render() {
let {shixunId} = this.state;
return (
<React.Fragment>
<div className="educontent">
@ -178,11 +79,8 @@ export default class TPMUpdatepropaede extends Component {
</div>
<div className="padding40-20">
<div className="padding10-20 edu-back-greyf5 radius4" id="UpdatepropaedeMD">
<textarea style={{display: 'none'}} id="Updatepropaedes" name="content"> </textarea>
<div className="CodeMirror cm-s-defualt">
</div>
</div>
<TPMMDEditor ref={this.neweditanswerRef} placeholder="请输入选择题的题干内容" mdID={'editquestioMDid'} refreshTimeout={1500}
needRecreate={true} watch={true} className="courseMessageMD" initValue={this.neweditanswerRefval}></TPMMDEditor>
</div>
</div>

@ -44,7 +44,7 @@
position:relative;
}
.newedboxheight{
max-height:204px;
max-height: 177px;
overflow-y: hidden;
}
.newminheight{

@ -9,6 +9,7 @@ import Modals from '../../modals/Modals';
import SendTopics from '../../modals/SendTopics'
import NoneData from '../../courses/coursesPublic/NoneData';
import "./usersInfo.css";
import Withoutpermission from './Withoutpermission.png';
@ -17,23 +18,36 @@ class InfosTopics extends Component{
super(props);
this.state={
isSpin:false,
category:"common",
category:"normal",
course_list_id:undefined,
sort_by:"updated_at",
sort_direction:"desc",
page:1,
data:undefined,
checkBoxValues:[],
per_page:15
per_page:15,
isshowprofes:false
}
}
componentDidMount(){
let types=this.props.match.params.topicstype;
let professional_certification=this.props.current_user&&this.props.current_user.professional_certification;
console.log(professional_certification)
if(professional_certification===false&&types==="publicly"){
this.setState({
isshowprofes:true
})
}else{
this.updataslist()
}
}
updataslist=()=>{
let types=this.props.match.params.topicstype;
let { category,course_list_id,sort_by,sort_direction,page}=this.state;
this.searchAlldata(types,category,course_list_id,sort_by,sort_direction,page)
}
searchAlldata=(type,category,course_list_id,sort_by,sort_direction,page)=>{
let user_id=this.props.current_user&&this.props.current_user.user_id;
if(user_id!=undefined){
@ -122,11 +136,16 @@ class InfosTopics extends Component{
let { category,course_list_id,sort_by,sort_direction,page}=this.state;
this.searchAlldata(types,category,course_list_id,sort_by,sort_direction,pageNumber)
this.setState({
page:pageNumber
page:pageNumber,
checkBoxValues:[]
})
}
deletecheckBoxValues=()=>{
let {checkBoxValues}=this.state;
if(checkBoxValues.length===0){
this.props.showNotification("请选择题库")
}
this.setState({
Modalstype:true,
Modalstopval:"是否确认删除?",
@ -136,13 +155,19 @@ class InfosTopics extends Component{
}
topicssavedelete=()=>{
let {checkBoxValues}=this.state;
const url = `/homework_commons/${workId}/student_works/delete_work.json`;
let {checkBoxValues,category}=this.state;
const url = `/question_banks/multi_delete.json`;
axios.delete(url, { data: {
user_id: id
object_id: checkBoxValues,
object_type:category
}})
.then((response) => {
if(response.data.status===0){
this.updataslist()
this.props.showNotification(response.data.message)
}else{
this.props.showNotification(response.data.message)
}
})
.catch(function (error) {
console.log(error);
@ -165,27 +190,31 @@ class InfosTopics extends Component{
})
}
openTopics=()=>{
openTopics=(id)=>{
this.setState({
Modalstype:true,
Modalstopval:"公开后不能重设为私有",
ModalsBottomval:"是否确认设为公开?",
ModalCancel:this.topicscancelmodel,
ModalSave:this.topicssaveonOpen,
ModalSave:()=>this.topicssaveonOpen(id),
})
}
topicssaveonOpen=()=>{
let {checkBoxValues}=this.state;
const url = `/homework_commons/${workId}/student_works/delete_work.json`;
axios.delete(url, { data: {
user_id: id
}})
.then((response) => {
})
.catch(function (error) {
topicssaveonOpen=(id)=>{
let {category}=this.state;
const url = `/question_banks/multi_public.json`;
axios.post(url,{
object_id:[id],
object_type:category
}).then((response) => {
if(response.data.status===0){
this.updataslist()
this.props.showNotification(response.data.message)
}else{
this.props.showNotification(response.data.message)
}
}).catch(function (error) {
console.log(error);
});
@ -194,9 +223,15 @@ class InfosTopics extends Component{
sendTopics=()=>{
this.setState({
visible:true
})
let {checkBoxValues}=this.state;
if(checkBoxValues.length===0){
this.props.showNotification("请选择题库")
}else{
this.setState({
visible:true
})
}
}
render(){
let{
@ -209,11 +244,12 @@ class InfosTopics extends Component{
sort_by,
checkBoxValues,
Modalstype,
visible
visible,
isshowprofes
} = this.state;
let categorylist=[
{val:"普通作业",type:"common"},
{val:"普通作业",type:"normal"},
{val:"分组作业",type:"group"},
{val:"毕设选题",type:"gtopic"},
{val:"毕设任务",type:"gtask"},
@ -223,11 +259,13 @@ class InfosTopics extends Component{
let types=this.props.match.params.topicstype;
console.log(Modalstype)
console.log(isshowprofes)
//types===publicly 公共
//types===personal 私有
let user_id=this.props.current_user&&this.props.current_user.user_id;
let user_type=this.props.current_user&&this.props.current_user.user_identity;
let targetuserid=this.props.data&&this.props.data.id;
const menu = (
<Menu>
<Menu.Item onClick={()=>this.updatedlist("updated_at")}>
@ -254,10 +292,14 @@ class InfosTopics extends Component{
loadtype={this.state.Loadtype}
/>:""}
{/*发送至弹窗*/}
{
visible&&visible===true?
<SendTopics
{...this.state}
{...this.props}
visible={visible}
updataslist={()=>this.updataslist()}
topicscancelmodel={()=>this.topicscancelmodel()}
/>:""
}
@ -301,40 +343,65 @@ class InfosTopics extends Component{
<Spin size="large" spinning={isSpin}>
<div className="clearfix topicsbox">
<div className={"topcschild"}>
{types==="publicly"?<div className={"topcschild"}>
<a className={types==="personal"?"topicstopfont fr topcsactive":"topicstopfont fr"}
href={`/users/innov/topics/personal`}>个人题库</a>
<a className={types==="publicly"?"topicstopfont fl topcsactive":"topicstopfont fl"}
href={`/users/innov/topics/publicly`}
>公共题库</a>
</div>:<div className={"topcschild"}>
<a className={types==="personal"?"topicstopfont fl topcsactive":"topicstopfont fl"}
href={`/users/innov/topics/personal`}>我的题库</a>
<a className={types==="publicly"?"topicstopfont fr topcsactive":"topicstopfont fr"}
href={`/users/innov/topics/publicly`}
>公共题库</a>
</div>
</div>}
<div className={"topcsmid"}>
{categorylist.map((item,key)=>{
return(
<span key={key} className={category===item.type?"topicsmidfont fl mr38 topcsactive":"topicsmidfont fl mr38"} onClick={()=>this.searchCategory(item.type)}>{item.val}</span>
)
})}
</div>
{isshowprofes===false?
<div>
<div className={"shaiContent"}>
<div className="fl pr topicsItem pagetype mb20">
<li className={course_list_id===undefined?"shaiItem shixun_repertoire active":"shaiItem shixun_repertoire"} onClick={()=>this.searchCourselistid(undefined)}>全部</li>
{data===undefined?"":data.course_list===undefined||data.course_list.length===0?"":data.course_list.map((item,key)=>{
<div className={"topcsmid"}>
{categorylist.map((item,key)=>{
return(
<li key={key} className={course_list_id===item.id?"shaiItem shixun_repertoire active":"shaiItem shixun_repertoire"} onClick={()=>this.searchCourselistid(item.id)}>{item.name}</li>
<span key={key} className={category===item.type?"topicsmidfont fl mr38 topcsactive":"topicsmidfont fl mr38"} onClick={()=>this.searchCategory(item.type)}>{item.val}</span>
)
})}
</div>
</div>
<div className={"shaiContent"}>
<div className="fl pr topicsItem pagetype mb20">
<li className={course_list_id===undefined?"shaiItem shixun_repertoire active":"shaiItem shixun_repertoire"} onClick={()=>this.searchCourselistid(undefined)}>全部</li>
{data===undefined?"":data.course_list===undefined||data.course_list.length===0?"":data.course_list.map((item,key)=>{
return(
<li key={key} className={course_list_id===item.id?"shaiItem shixun_repertoire active":"shaiItem shixun_repertoire"} onClick={()=>this.searchCourselistid(item.id)}>{item.name}</li>
)
})}
</div>
</div>
</div>:<div className={"professional_certificationbox"}>
<p className="clearfix ">
<div className={"stud-class-set pd115200 coursenavbox edu-back-white"}>
<div className={"sumbtongs mb10"}><img className={"topicsItemimg"} src={Withoutpermission}/></div>
<div className={"terraces mb5 topicsItemfont"}>通过职业认证的教师才能访问公共题库</div>
<div className="clearfix mt30 mb30 padding251">
<a className="defalutSubmitbtn fl ml60 defalutSubmitbtns" target="_blank" href="/account/certification">立即认证</a>
</div>
</div>
</p>
</div>}
</div>
<div className="clearfix font-12 mt20">
{isshowprofes===false?<div className="clearfix font-12 mt20">
<p className="font-12 alltopisc ml25 fl">
<span className="fl color-grey-9"> <span className={"color-orange"}>{data&&data.count}</span> </span>
<span className="fl color-grey-9"> <span className={"color-orange"}>{data&&data.count===undefined?0:data&&data.count}</span> </span>
<span className="fr color-grey-9">已选择 <span className={"color-orange"}>{checkBoxValues.length}</span> ()</span>
</p>
<p className="font-12 alltopiscright ml25 fr">
@ -349,13 +416,13 @@ class InfosTopics extends Component{
</sapn>
</span>
</Dropdown>
<span className="fr mr30 topcsactive pointer" onClick={()=>this.sendTopics()}>发送</span>
{types==="personal"?<span className="fr mr30 topcsactive pointer" onClick={()=>this.deletecheckBoxValues()}>删除</span>:""}
{user_type!="学生"?<span className="fr mr30 topcsactive pointer" onClick={()=>this.sendTopics()}>发送</span>:""}
{types==="personal"?user_id===targetuserid&&user_type!="学生"?<span className="fr mr30 topcsactive pointer" onClick={()=>this.deletecheckBoxValues()}>删除</span>:"":""}
</p>
</div>
</div>:""}
{data===undefined?<NoneData></NoneData>:data.question_banks===undefined||data.question_banks.length===0?<NoneData></NoneData>:
{isshowprofes===true?"":data===undefined?<NoneData></NoneData>:data.question_banks===undefined||data.question_banks.length===0?<NoneData></NoneData>:
<Checkbox.Group style={{ width: '100%' }} onChange={this.onCheckBoxChange} value={checkBoxValues}>
{data.question_banks.map((item,key)=>{
return(
@ -364,14 +431,14 @@ class InfosTopics extends Component{
<div className="item-body">
<div className="clearfix ds pr pt5 contentSection" >
<Checkbox value={item.id} className={"fl mt5"}></Checkbox>
{user_type!="学生"?<Checkbox value={item.id} key={item.id} className={"fl mt5"}></Checkbox>:""}
<a title={item.name} className="ml10 fl mt3 font-16 color-dark maxwidth900">
{item.name}
</a>
{item.is_public===false?<span className="edu-filter-btn ml15 fl typestyle mt3 topiscfilterbtn">公开</span>:""}
{item.is_public===true?<span className="edu-filter-btn ml15 fl typestyle mt3 topiscfilterbtn">公开</span>:""}
{types==="personal"&&item.is_public===false?<a className="btn colorblue mr25 fr font-16" onClick={()=>this.openTopics()}>设为公开</a>:""}
{types==="personal"&&item.is_public===false?user_id===targetuserid&&user_type!="学生"?<a className="btn colorblue mr25 fr font-16" onClick={()=>this.openTopics(item.id)}>设为公开</a>:"":""}
<div className="cl"></div>
@ -386,7 +453,17 @@ class InfosTopics extends Component{
</p>
<div className="homepagePostSetting homepagePostSettingname topscisright">
{types==="personal"?<a className="btn colorblue mr25 font-16 fr">编辑</a>:""}
{types==="personal"?user_id===targetuserid&&user_type!="学生"?
<a className="btn colorblue mr25 font-16 fr"
href={category==="normal"?`/courses/ordinarywork/${item.id}?tab=0`:
category==="group"?`/courses/groupingwork/${item.id}?tab=0`:
category==="poll"?`/courses/poll/${item.id}`:
category==="exercise"?`/courses/poll/${item.id}`:
category==="gtask"?`/courses/completetask/${item.id}`:
category==="gtopic"?`/courses/completetopic/${item.id}`:""
}
>编辑</a>
:"":""}
</div>
</div>
</div>
@ -398,7 +475,7 @@ class InfosTopics extends Component{
}
{
data&&data.count >15 &&
isshowprofes===true?"":data&&data.count >15 &&
<div className="mt30 mb50 edu-txt-center">
<Pagination showQuickJumper total={data&&data.count} onChange={this.changePage} pageSize={15} current={page}/>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

@ -70,7 +70,7 @@ class BanksIndex extends Component{
<p className="clearfix mt20 mb20">
<span className="fl font-24 color-grey-3 task-hide lineh-30" style={{maxWidth:'800px'}}>{crumbData && crumbData.title}</span>
{
crumbData && crumbData.is_public && <span className="bank_is_public">{crumbData.is_public == true ? '公开':'私有'}</span>
crumbData && <span className="bank_is_public">{crumbData.is_public == true ? '公开':'私有'}</span>
}
</p>
@ -89,7 +89,12 @@ class BanksIndex extends Component{
return (<BanksTabIndex {...this.props} {...props} {...this.state} {...common}/>)
}
}></Route>
<Route path='/banks/poll/:bankId'
render={
(props) => {
return (<BanksTabIndex {...this.props} {...props} {...this.state} {...common} />)
}
}></Route>
</Switch>
</div>

@ -12,6 +12,11 @@ const GtopicBanks = Loadable({
loader: () => import('./GtopicBanks'),
loading: Loading,
})
// 问卷内容
const PollBanks = Loadable({
loader: () => import('./PollBanksContent'),
loading: Loading,
})
class BanksTabIndex extends Component{
constructor(props){
@ -56,6 +61,12 @@ class BanksTabIndex extends Component{
return (<GtopicBanks {...this.props} {...props} {...this.state} {...common} />)
}
}></Route>
<Route path='/banks/poll/:bankId'
render={
(props) => {
return (<PollBanks {...this.props} {...props} {...this.state} {...common} />)
}
}></Route>
</Switch>
</React.Fragment>

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

Loading…
Cancel
Save