Merge branch 'dev_aliyun' of https://bdgit.educoder.net/Hjqreturn/educoder into dev_aliyun

dev_aliyun_beta
SylorHuang 5 years ago
commit b7846f789e

@ -1,21 +1,20 @@
$(document).on('turbolinks:load', function() { $(document).on('turbolinks:load', function() {
if ($('body.admins-library-applies-index-page').length > 0) { if ($('body.admins-library-applies-index-page').length > 0) {
var $searchFrom = $('.library-applies-list-form'); var $searchFrom = $('.library-applies-list-form');
$searchFrom.find('select[name="status"]').val('pending'); $searchFrom.find('select[name="status"]').val('pending');
$searchFrom.on('click', '.search-form-tab', function(){
var $link = $(this);
$searchFrom.find('input[name="keyword"]').val(''); $searchFrom.on('click', '.search-form-tab', function(){
$searchFrom.find('select[name="status"]').val('processed'); var $link = $(this);
if($link.data('value') === 'processed'){ $searchFrom.find('input[name="keyword"]').val('');
$searchFrom.find('.status-filter').show(); $searchFrom.find('select[name="status"]').val('processed');
} else {
$searchFrom.find('.status-filter').hide();
$searchFrom.find('select[name="status"]').val('pending');
}
});
} 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() { $(document).on('turbolinks:load', function() {
if ($('body.admins-shixun-settings-index-page').length > 0) { 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_value = $(this).val();
var s_name = $(this).attr("name"); var s_name = $(this).attr("name");
var json = {}; 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_id = $(this).attr("data-id");
var s_value = $(this).val(); var s_value = $(this).val();
var s_name = $(this).attr("name"); var s_name = $(this).attr("name");

@ -1,6 +1,8 @@
$(document).on('turbolinks:load', function() { $(document).on('turbolinks:load', function() {
if($('body.admins-shixuns-index-page').length > 0){
$('select#tag-choosed').select2({ $('select#tag-choosed').select2({
placeholder: "请选择分类", placeholder: "请选择分类",
allowClear: true 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 "bootstrap-datepicker.standalone";
@import "lib/codemirror"; @import "lib/codemirror";
@import "common";
@import "admins/*"; @import "admins/*";
body { body {
@ -20,20 +21,6 @@ body {
background: #efefef; background: #efefef;
} }
a {
&:hover {
text-decoration: unset;
}
}
textarea.danger, input.danger {
border-color: #dc3545!important;
}
label.error {
color: #dc3545!important;
}
.simple_form { .simple_form {
.form-group { .form-group {
.collection_radio_buttons { .collection_radio_buttons {
@ -50,9 +37,6 @@ input.form-control {
font-size: 14px; font-size: 14px;
} }
.flex-1 {
flex: 1;
}
.btn-default{ .btn-default{
color: #666; color: #666;
background: #e1e1e1!important; background: #e1e1e1!important;
@ -62,8 +46,3 @@ input.form-control {
position: absolute; position: absolute;
} }
.position-r{position:relative;} .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

@ -8,38 +8,12 @@ class ExerciseBanksController < ApplicationController
def show def show
@exercise_questions = @bank.exercise_bank_questions&.includes(:exercise_bank_choices, :exercise_bank_shixun_challenges, @exercise_questions = @bank.exercise_bank_questions&.includes(:exercise_bank_choices, :exercise_bank_shixun_challenges,
:exercise_bank_standard_answers).order("question_number ASC") :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
# 判断题数量及分数 if @bank.container_type == "Exercise"
exercise_ques_judge = @exercise_questions.find_by_custom("question_type", Exercise::JUDGMENT) get_exercise_question_count
@exercise_ques_judge_count = exercise_ques_judge.size else
@exercise_ques_judge_scores = exercise_ques_judge.pluck(:question_score).sum get_poll_question_count
end
#填空题数量及分数
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 end
def update def update
@ -103,4 +77,46 @@ class ExerciseBanksController < ApplicationController
end end
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 end

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

@ -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) } scope :without_deleted, -> { where(is_delete: false) }
def member?(user)
department_members.exists?(user_id: user.id)
end
def soft_delete! def soft_delete!
update!(is_delete: true) update!(is_delete: true)
end end

@ -3,7 +3,7 @@ class HomeworkCommon < ApplicationRecord
enum homework_type: { normal: 1, program: 2, group: 3, practice: 4 }, _suffix: true enum homework_type: { normal: 1, program: 2, group: 3, practice: 4 }, _suffix: true
has_many :homework_group_settings, dependent: :destroy has_many :homework_group_settings, dependent: :destroy
has_many :published_settings, -> { group_published }, class_name: "HomeworkGroupSetting" 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_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 has_one :homework_detail_manual, dependent: :destroy

@ -1,3 +1,5 @@
class Partner < ApplicationRecord class Partner < ApplicationRecord
belongs_to :school, optional: true
has_many :users has_many :users
end 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 :school_daily_reports, dependent: :destroy
has_many :courses has_many :courses
has_many :customers, dependent: :destroy
has_many :partners, dependent: :destroy
# 学校管理员 # 学校管理员
def manager?(user) def manager?(user)
ec_school_users.exists?(user_id: user.id) ec_school_users.exists?(user_id: user.id)

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

@ -15,7 +15,7 @@ class Admins::DepartmentQuery < ApplicationQuery
keyword = params[:keyword].to_s.strip keyword = params[:keyword].to_s.strip
if keyword.present? if keyword.present?
departments = departments.joins(:school) 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 end
if params[:with_member].to_s == 'true' if params[:with_member].to_s == 'true'

@ -14,7 +14,13 @@
<td class="member-container"> <td class="member-container">
<%= render partial: 'admins/departments/shared/member_users', locals: { department: department } %> <%= render partial: 'admins/departments/shared/member_users', locals: { department: department } %>
</td> </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.host_count %></td>
<td><%= department.created_at&.strftime('%Y-%m-%d %H:%M') %></td> <td><%= department.created_at&.strftime('%Y-%m-%d %H:%M') %></td>
<td class="action-container"> <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 if @bank.container_type == "Exercise"
json.extract! @bank, :id, :name, :description, :is_public json.exercise do
end 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
json.partial! "exercises/exercise_scores" else
json.poll do
json.extract! @bank, :id, :name, :description, :is_public
end
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.questions do
json.array! @exercise_questions do |q| json.array! @exercise_questions do | question|
json.partial! "exercise_bank_questions/exercise_bank_questions", json.partial! "exercise_banks/poll_questions", question: question, answers: question.exercise_bank_choices
question: q, end
choices:q.exercise_bank_choices,
shixun_challenges: q.exercise_bank_shixun_challenges,
ques_position:nil,
edit_type:nil
end end
end end

@ -4,6 +4,7 @@
<title>EduCoder后台管理</title> <title>EduCoder后台管理</title>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <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 %> <%= csrf_meta_tags %>
<%= csp_meta_tag %> <%= 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>

@ -12,5 +12,5 @@ Rails.application.config.assets.paths << Rails.root.join('vendor/assets')
# Precompile additional assets. # Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in the app/assets # application.js, application.css, and all non-JS/CSS in the app/assets
# folder are already added. # 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 )

@ -848,6 +848,19 @@ Rails.application.routes.draw do
end end
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 认证回调 #git 认证回调
match 'gitauth/*url', to: 'gits#auth', via: :all 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() { $(document).on('turbolinks:load', function() {
if ($('body.admins-library-applies-index-page').length > 0) { if ($('body.admins-library-applies-index-page').length > 0) {
var $searchFrom = $('.library-applies-list-form'); var $searchFrom = $('.library-applies-list-form');
$searchFrom.find('select[name="status"]').val('pending'); $searchFrom.find('select[name="status"]').val('pending');
$searchFrom.on('click', '.search-form-tab', function(){
var $link = $(this);
$searchFrom.find('input[name="keyword"]').val(''); $searchFrom.on('click', '.search-form-tab', function(){
$searchFrom.find('select[name="status"]').val('processed'); var $link = $(this);
if($link.data('value') === 'processed'){ $searchFrom.find('input[name="keyword"]').val('');
$searchFrom.find('.status-filter').show(); $searchFrom.find('select[name="status"]').val('processed');
} else {
$searchFrom.find('.status-filter').hide();
$searchFrom.find('select[name="status"]').val('pending');
}
});
} 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() { $(document).on('turbolinks:load', function() {
@ -38362,7 +38361,7 @@ $(document).on('turbolinks:load', function() {
; ;
$(document).on('turbolinks:load', function() { $(document).on('turbolinks:load', function() {
if ($('body.admins-shixun-settings-index-page').length > 0) { 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_value = $(this).val();
var s_name = $(this).attr("name"); var s_name = $(this).attr("name");
var json = {}; 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_id = $(this).attr("data-id");
var s_value = $(this).val(); var s_value = $(this).val();
var s_name = $(this).attr("name"); var s_name = $(this).attr("name");
@ -38394,10 +38393,12 @@ $(document).on('turbolinks:load', function() {
}); });
$(document).on('turbolinks:load', function() { $(document).on('turbolinks:load', function() {
if($('body.admins-shixuns-index-page').length > 0){
$('select#tag-choosed').select2({ $('select#tag-choosed').select2({
placeholder: "请选择分类", placeholder: "请选择分类",
allowClear: true allowClear: true
}); });
}
}); });
$(document).on('turbolinks:load', function(){ $(document).on('turbolinks:load', function(){
$('#sidebarCollapse').on('click', 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

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

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

@ -493,7 +493,8 @@ class Fileslists extends Component{
let selectpagetype=selectpage===page?true:false let selectpagetype=selectpage===page?true:false
this.setState({ this.setState({
page:page, page:page,
checkAllValue:selectpagetype checkAllValue:selectpagetype,
checkBoxValues:[]
}) })
let{pagesize,tagname,searchValue,sort,sorttype,coursesecondcategoryid}=this.state; let{pagesize,tagname,searchValue,sort,sorttype,coursesecondcategoryid}=this.state;
@ -801,7 +802,7 @@ class Fileslists extends Component{
{this.props.isAdmin()? files===undefined?'' :files.length===0? "":<div className="mt20 edu-back-white padding20-30" style={{display:this.props.isAdmin()||this.props.isStudent()?"":"none"}}> {this.props.isAdmin()? files===undefined?'' :files.length===0? "":<div className="mt20 edu-back-white padding20-30" style={{display:this.props.isAdmin()||this.props.isStudent()?"":"none"}}>
<div className="clearfix"> <div className="clearfix">
{this.props.isAdmin()? <Checkbox className="fl" onChange={this.onCheckAll} checked={checkAllValue}>已选 {checkBoxValues.length} </Checkbox>:""} {this.props.isAdmin()? <Checkbox className="fl" onChange={this.onCheckAll} checked={checkAllValue}>已选 {checkBoxValues.length} 不支持跨页勾选</Checkbox>:""}
<div className="studentList_operation_ul"> <div className="studentList_operation_ul">
{this.props.isAdmin()?<li className="li_line"><a href="javascript:void(0)" className="color-grey-9" onClick={this.onDelete}>删除</a></li>:""} {this.props.isAdmin()?<li className="li_line"><a href="javascript:void(0)" className="color-grey-9" onClick={this.onDelete}>删除</a></li>:""}
{this.props.isAdmin()?<li className="li_line"><a href="javascript:void(0)" className="color-grey-9" onClick={this.onSend}>发送</a></li>:""} {this.props.isAdmin()?<li className="li_line"><a href="javascript:void(0)" className="color-grey-9" onClick={this.onSend}>发送</a></li>:""}
@ -837,7 +838,7 @@ class Fileslists extends Component{
{course_modules&&course_modules.course_modules.map((item,key)=>{ {course_modules&&course_modules.course_modules.map((item,key)=>{
return( 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>
) )
})} })}
@ -847,7 +848,7 @@ class Fileslists extends Component{
return (!this.state.dirSearchValue || item.name.indexOf(this.state.dirSearchValue) != -1) return (!this.state.dirSearchValue || item.name.indexOf(this.state.dirSearchValue) != -1)
}).map((itm,k)=>{ }).map((itm,k)=>{
return( 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>
) )
}) })
})} })}

@ -283,6 +283,9 @@ class Boards extends Component{
console.log('checked = ', checkedValues); console.log('checked = ', checkedValues);
} }
onPageChange = (pageNumber) => { onPageChange = (pageNumber) => {
this.setState({
checkBoxValues:[]
})
this.fetchAll(null, pageNumber) this.fetchAll(null, pageNumber)
} }
@ -365,7 +368,7 @@ class Boards extends Component{
{messages&&messages.length == 0?"": isAdmin && <div className="mt20 edu-back-white padding20-30"> {messages&&messages.length == 0?"": isAdmin && <div className="mt20 edu-back-white padding20-30">
<div className="clearfix"> <div className="clearfix">
{isAdmin&&<Checkbox className="fl" onChange={this.onCheckAll} checked={checkAllValue}>已选 {checkBoxValues.length} </Checkbox>} {isAdmin&&<Checkbox className="fl" onChange={this.onCheckAll} checked={checkAllValue}>已选 {checkBoxValues.length} 不支持跨页勾选</Checkbox>}
<div className="studentList_operation_ul"> <div className="studentList_operation_ul">
{ !!isAdmin && { !!isAdmin &&
<React.Fragment> <React.Fragment>
@ -384,7 +387,7 @@ class Boards extends Component{
boards && boards.filter((item)=> { boards && boards.filter((item)=> {
return item.id != bid && (!this.state.dirSearchValue || item.name.indexOf(this.state.dirSearchValue) != -1) return item.id != bid && (!this.state.dirSearchValue || item.name.indexOf(this.state.dirSearchValue) != -1)
}).map( (item) => { }).map( (item) => {
return <li onClick={() => this.moveTo(item)}>{item.name}</li> return <li onClick={() => this.moveTo(item)} title={item.name}>{item.name}</li>
}) })
} }
{ isAdmin && { isAdmin &&

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

@ -153,7 +153,8 @@ class commonWork extends Component{
onPageChange=(pageNumber)=>{ onPageChange=(pageNumber)=>{
this.setState({ this.setState({
page:pageNumber page:pageNumber,
checkBoxValues:[]
}) })
let {search,order}=this.state; let {search,order}=this.state;
this.getList(pageNumber,search,order); this.getList(pageNumber,search,order);
@ -430,7 +431,7 @@ class commonWork extends Component{
mainList && mainList.course_identity < 5 && mainList.homeworks.length>0 && mainList && mainList.course_identity < 5 && mainList.homeworks.length>0 &&
<div className="mt20 edu-back-white padding20-30"> <div className="mt20 edu-back-white padding20-30">
<div className="clearfix" > <div className="clearfix" >
<Checkbox className="fl" onChange={this.changeAll} checked={checkAll}>已选 {checkBoxValues.length} </Checkbox> <Checkbox className="fl" onChange={this.changeAll} checked={checkAll}>已选 {checkBoxValues.length} 不支持跨页勾选</Checkbox>
<div className="studentList_operation_ul"> <div className="studentList_operation_ul">
<li className="li_line"> <li className="li_line">
<a href="javascript:void(0)" className="color-grey-9" <a href="javascript:void(0)" className="color-grey-9"

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

@ -150,8 +150,7 @@ class Startshixuntask extends Component{
keyboard={false} keyboard={false}
> >
<div className="task-popup-content"> <div className="task-popup-content">
<p className="task-popup-text-center font-16 pb20">本实训的开启时间{shixunsmessage} <br/>开启时间之前不能挑战 <p className="task-popup-text-center font-16 pb20">目前该实训项目尚在内测中将于{shixunsmessage}之后开放谢谢</p>
</p>
</div> </div>
<div className="task-popup-submit clearfix"> <div className="task-popup-submit clearfix">
{/*<a onClick={this.hidestartshixunCombattype} className="task-btn fl">取消</a>*/} {/*<a onClick={this.hidestartshixunCombattype} className="task-btn fl">取消</a>*/}

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

@ -293,7 +293,7 @@ class Elearning extends Component{
footer={null} footer={null}
> >
<div className="task-popup-content"> <div className="task-popup-content">
<p className="task-popup-text-center font-16 pb20">本实训的开启时间{this.state.shixunsmessages} <br/>开启时间之前不能挑战 </p> <p className="task-popup-text-center font-16 pb20">目前该实训项目尚在内测中将于{this.state.shixunsmessages}之后开放谢谢</p>
</div> </div>
<div className="task-popup-submit clearfix"> <div className="task-popup-submit clearfix">
{/*<a onClick={this.hidestartshixunCombattype} className="task-btn fl">取消</a>*/} {/*<a onClick={this.hidestartshixunCombattype} className="task-btn fl">取消</a>*/}

@ -141,7 +141,7 @@ class YslDetailCards extends Component{
footer={null} footer={null}
> >
<div className="task-popup-content"> <div className="task-popup-content">
<p className="task-popup-text-center font-16 pb20">本实训的开启时间{this.state.shixunsmessage} <br/>开启时间之前不能挑战 </p> <p className="task-popup-text-center font-16 pb20">目前该实训项目尚在内测中将于{this.state.shixunsmessage}之后开放谢谢</p>
</div> </div>
<div className="task-popup-submit clearfix"> <div className="task-popup-submit clearfix">
{/*<a onClick={this.hidestartshixunCombattype} className="task-btn fl">取消</a>*/} {/*<a onClick={this.hidestartshixunCombattype} className="task-btn fl">取消</a>*/}

@ -130,7 +130,8 @@ class Exercise extends Component{
//切换分页 //切换分页
changePage=(pageNumber)=>{ changePage=(pageNumber)=>{
this.setState({ this.setState({
page:pageNumber page:pageNumber,
checkBoxValues:[]
}) })
let{type,StudentList_value,limit}=this.state let{type,StudentList_value,limit}=this.state
this.InitList(type,StudentList_value,pageNumber,limit); this.InitList(type,StudentList_value,pageNumber,limit);
@ -522,7 +523,7 @@ class Exercise extends Component{
<Spin size="large" spinning={this.state.isSpin}> <Spin size="large" spinning={this.state.isSpin}>
{this.props.isAdmin()?exercises && exercises.length ===0?"":<div className="mt20 mb20 edu-back-white padding20-30"> {this.props.isAdmin()?exercises && exercises.length ===0?"":<div className="mt20 mb20 edu-back-white padding20-30">
<div className="clearfix"> <div className="clearfix">
<Checkbox className="fl" onChange={this.onCheckAll} checked={checkAllValue}>已选 {checkBoxValues.length} </Checkbox> <Checkbox className="fl" onChange={this.onCheckAll} checked={checkAllValue}>已选 {checkBoxValues.length} 不支持跨页勾选</Checkbox>
<div className="studentList_operation_ul"> <div className="studentList_operation_ul">
<li className="li_line"><a className="color-grey-9" onClick={()=>this.ActionPoll("delete")}>删除</a></li> <li className="li_line"><a className="color-grey-9" onClick={()=>this.ActionPoll("delete")}>删除</a></li>
<li className="li_line"> <li className="li_line">

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

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

@ -360,7 +360,8 @@ class GraduationTasks extends Component{
let selectpagetype=selectpage===page?true:false let selectpagetype=selectpage===page?true:false
this.setState({ this.setState({
page:page, page:page,
checkAllValue:selectpagetype checkAllValue:selectpagetype,
checkBoxValues:[]
}) })
this.fetchAll(search,page,order); this.fetchAll(search,page,order);
@ -700,7 +701,7 @@ class GraduationTasks extends Component{
{this.props.isAdmin()?all_count===undefined?'' :all_count===0?"": <div className="mt20 edu-back-white padding20-30"> {this.props.isAdmin()?all_count===undefined?'' :all_count===0?"": <div className="mt20 edu-back-white padding20-30">
<div className="clearfix"> <div className="clearfix">
<Checkbox className="fl" onChange={this.onCheckAll} checked={checkAllValue}>已选 {checkBoxValues.length} </Checkbox> <Checkbox className="fl" onChange={this.onCheckAll} checked={checkAllValue}>已选 {checkBoxValues.length} 不支持跨页勾选</Checkbox>
<div className="studentList_operation_ul"> <div className="studentList_operation_ul">
<li className="li_line"><a className="color-grey-9" onClick={this.onDelete}>删除</a></li> <li className="li_line"><a className="color-grey-9" onClick={this.onDelete}>删除</a></li>

@ -153,7 +153,8 @@ class Boards extends Component{
// 分页 // 分页
onChangePage=(pageNum)=>{ onChangePage=(pageNum)=>{
this.setState({ this.setState({
page:pageNum page:pageNum,
checkBoxValues:[]
}) })
let {status,searchValue}=this.state; let {status,searchValue}=this.state;
this.fetchAll(searchValue,pageNum,status); this.fetchAll(searchValue,pageNum,status);
@ -430,7 +431,7 @@ onBoardsNew=()=>{
// 超级管理员、教师、助教 // 超级管理员、教师、助教
isAdmin ? isAdmin ?
<div className="clearfix mt20 edu-back-white padding20-30"> <div className="clearfix mt20 edu-back-white padding20-30">
<Checkbox className="fl" onChange={this.onCheckAll} checked={checkAllValue}>已选 {checkBoxValues.length} </Checkbox> <Checkbox className="fl" onChange={this.onCheckAll} checked={checkAllValue}>已选 {checkBoxValues.length} 不支持跨页勾选</Checkbox>
<div className="studentList_operation_ul"> <div className="studentList_operation_ul">
<li className="li_line"><a href="javascript:void(0)" className="color-grey-9" onClick={()=>this.onDelete(1)}>删除</a></li> <li className="li_line"><a href="javascript:void(0)" className="color-grey-9" onClick={()=>this.onDelete(1)}>删除</a></li>
{ {

@ -7,8 +7,7 @@ import CourseLayoutcomponent from '../common/CourseLayoutComponent'
import Titlesearchsection from '../common/titleSearch/TitleSearchSection' import Titlesearchsection from '../common/titleSearch/TitleSearchSection'
import ColorCountText from '../common/titleSearch/ColorCountText' import ColorCountText from '../common/titleSearch/ColorCountText'
import { WordsBtn, trigger, on, off, getUrl, downloadFile } from 'educoder'
import { WordsBtn, trigger, on, off ,downloadFile} from 'educoder'
import Modals from "../../modals/Modals"; import Modals from "../../modals/Modals";
import axios from 'axios' import axios from 'axios'
import _ from 'lodash' import _ from 'lodash'
@ -151,10 +150,19 @@ class studentsList extends Component{
}) })
} }
}else { }else {
this.props.showNotification(`正在下载中`);
window.open("/api"+url, '_blank'); // this.props.showNotification(`正在下载中`);
// window.open("/api"+url, '_blank');
this.props.slowDownload(url)
// getUrl() + "/api"+
// const fileUrl = url;
// this.props.slowDownload(fileUrl)
// return;
// downloadFile({ // downloadFile({
// url: url, // url: fileUrl,
// successCallback: (url) => { // successCallback: (url) => {
// console.log('successCallback') // console.log('successCallback')
// }, // },
@ -162,6 +170,7 @@ class studentsList extends Component{
// console.log('failCallback') // console.log('failCallback')
// } // }
// }) // })
// window.open(fileUrl, "_self");// , '_blank'
} }
}).catch((error) => { }).catch((error) => {
console.log(error) console.log(error)
@ -650,7 +659,7 @@ class studentsList extends Component{
return item.id != course_group_id && (!this.state.groupSearchValue || item.name.indexOf(this.state.groupSearchValue) != -1) return item.id != course_group_id && (!this.state.groupSearchValue || item.name.indexOf(this.state.groupSearchValue) != -1)
}).map( item => { }).map( item => {
return ( 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 && { isAdmin &&

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

@ -151,7 +151,8 @@ class Poll extends Component{
changePage=(pageNumber)=>{ changePage=(pageNumber)=>{
this.setState({ this.setState({
page:pageNumber page:pageNumber,
checkBoxValues:[]
}) })
let{type,StudentList_value}=this.state let{type,StudentList_value}=this.state
this.InitList(type,StudentList_value,pageNumber); this.InitList(type,StudentList_value,pageNumber);
@ -543,7 +544,7 @@ class Poll extends Component{
pollsList && pollsList.length > 0 && isAdmin && pollsList && pollsList.length > 0 && isAdmin &&
<div className="mt20 edu-back-white padding20-30"> <div className="mt20 edu-back-white padding20-30">
<div className="clearfix"> <div className="clearfix">
<Checkbox className="fl" onChange={this.onCheckAll} checked={checkAllValue}>已选 {checkBoxValues.length} </Checkbox> <Checkbox className="fl" onChange={this.onCheckAll} checked={checkAllValue}>已选 {checkBoxValues.length} 不支持跨页勾选</Checkbox>
<div className="studentList_operation_ul"> <div className="studentList_operation_ul">
<li className="li_line"><a className="color-grey-9" onClick={()=>this.ActionPoll("delete")}>删除</a></li> <li className="li_line"><a className="color-grey-9" onClick={()=>this.ActionPoll("delete")}>删除</a></li>
<li className="li_line"> <li className="li_line">

@ -2146,8 +2146,19 @@ class PollNew extends Component {
} else { } else {
for (var i = 0; i < arr.length; i++) { for (var i = 0; i < arr.length; i++) {
if (index === i) { if (index === i) {
arr[i].question.min_choices = parseInt(value); try {
arr[i].question.max_choices = length; 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({ this.setState({

@ -218,8 +218,7 @@ class ShixunhomeWorkItem extends Component{
footer={null} footer={null}
> >
<div className="task-popup-content"> <div className="task-popup-content">
<p className="task-popup-text-center font-16 pb20">本实训的开启时间{shixunsmessage} <br/>开启时间之前不能挑战 <p className="task-popup-text-center font-16 pb20">目前该实训项目尚在内测中将于{shixunsmessage}之后开放谢谢</p>
</p>
</div> </div>
<div className="task-popup-submit clearfix"> <div className="task-popup-submit clearfix">
{/*<a onClick={this.hidestartshixunCombattype} className="task-btn fl">取消</a>*/} {/*<a onClick={this.hidestartshixunCombattype} className="task-btn fl">取消</a>*/}

@ -54,7 +54,8 @@ class ShixunHomework extends Component{
course_groupslist:[], course_groupslist:[],
checkedtype:false, checkedtype:false,
checkBoxValues:[], checkBoxValues:[],
isSpin:false isSpin:false,
antIcon:false
} }
} }
updateNavSuccess=()=>{ updateNavSuccess=()=>{
@ -559,7 +560,8 @@ class ShixunHomework extends Component{
let {Coursename,order}=this.state; let {Coursename,order}=this.state;
this.setState({ this.setState({
page:pageNumber page:pageNumber,
checkBoxValues:[]
}) })
this.homeworkupdatalist(Coursename,pageNumber,order); this.homeworkupdatalist(Coursename,pageNumber,order);
@ -659,10 +661,11 @@ class ShixunHomework extends Component{
savedelete=()=>{ savedelete=()=>{
this.setState({
antIcon:true
})
let {Coursename,page,order,checkBoxValues,datas}=this.state; let {Coursename,page,order,checkBoxValues,datas}=this.state;
let category_id=this.props.match.params.category_id; let category_id=this.props.match.params.category_id;
const cid = this.props.match.params.coursesId const cid = this.props.match.params.coursesId
const url = `/courses/`+cid+`/homework_commons/multi_destroy.json`; const url = `/courses/`+cid+`/homework_commons/multi_destroy.json`;
axios.post(url, { axios.post(url, {
@ -690,11 +693,17 @@ class ShixunHomework extends Component{
Loadtype:false, Loadtype:false,
checkBoxValues:[], checkBoxValues:[],
checkedtype:false, checkedtype:false,
antIcon:false
}) })
this.props.showNotification(response.data.message) this.props.showNotification(response.data.message)
this.homeworkupdatalist(Coursename,page,order); this.homeworkupdatalist(Coursename,page,order);
this.props.updataleftNavfun() this.props.updataleftNavfun()
} }else{
this.setState({
antIcon:false
})
this.props.showNotification(response.data.message)
}
}) })
.catch(function (error) { .catch(function (error) {
console.log(error); console.log(error);
@ -922,6 +931,7 @@ class ShixunHomework extends Component{
course_modules, course_modules,
shixunpath, shixunpath,
order, order,
antIcon,
}=this.state; }=this.state;
let main_id=this.props.match.params.main_id; let main_id=this.props.match.params.main_id;
@ -937,6 +947,7 @@ class ShixunHomework extends Component{
modalSave={this.state.ModalSave} modalSave={this.state.ModalSave}
modalsBottomval={this.state.ModalsBottomval} modalsBottomval={this.state.ModalsBottomval}
loadtype={this.state.Loadtype} loadtype={this.state.Loadtype}
antIcon={this.state.antIcon}
/>:""} />:""}
{/*立即发布*/} {/*立即发布*/}
{visible===true?<HomeworkModal {visible===true?<HomeworkModal
@ -1070,7 +1081,7 @@ class ShixunHomework extends Component{
<div className="mt20 edu-back-white padding20-30"> <div className="mt20 edu-back-white padding20-30">
<div className="clearfix"> <div className="clearfix">
<Checkbox className="fl" style={{marginTop:'0px'}}checked={checkedtype} onClick={this.funselect}>已选 {checkBoxValues&&checkBoxValues.length} </Checkbox> <Checkbox className="fl" style={{marginTop:'0px'}}checked={checkedtype} onClick={this.funselect}>已选 {checkBoxValues&&checkBoxValues.length} 不支持跨页勾选</Checkbox>
<div className="studentList_operation_ul"> <div className="studentList_operation_ul">
<li className="li_line"><a className="color-grey-9" onClick={this.onDelete}>删除</a></li> <li className="li_line"><a className="color-grey-9" onClick={this.onDelete}>删除</a></li>
<li className="li_line"><a className="color-grey-9" onClick={this.homeworkstart}>立即发布</a></li> <li className="li_line"><a className="color-grey-9" onClick={this.homeworkstart}>立即发布</a></li>
@ -1089,7 +1100,7 @@ class ShixunHomework extends Component{
{course_modules&&course_modules.main_category.map((item,key)=>{ {course_modules&&course_modules.main_category.map((item,key)=>{
return( 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>
) )
})} })}
@ -1098,10 +1109,10 @@ class ShixunHomework extends Component{
return (!this.state.dirSearchValue || item.category_name.indexOf(this.state.dirSearchValue) != -1) return (!this.state.dirSearchValue || item.category_name.indexOf(this.state.dirSearchValue) != -1)
}).map( (item,key) => { }).map( (item,key) => {
if(datas&&datas.category_id!=null&&datas&&datas.category_id===item.category_id===false){ 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){ 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: '' errorMsg: ''
}) })
}) })
commentMDEditor.cm.focus()
}, { }, {
watch: false, watch: false,
dialogLockScreen: false, dialogLockScreen: false,
@ -124,7 +125,7 @@ class MemoDetailMDEditor extends Component {
this.initMDEditor() this.initMDEditor()
} else { } else {
setTimeout(() => { setTimeout(() => {
this.commentMDEditor && this.commentMDEditor.focus() this.commentMDEditor && this.commentMDEditor.cm.focus()
}, 10) }, 10)
} }
} }

@ -361,7 +361,7 @@ class DetailCards extends Component{
footer={null} footer={null}
> >
<div className="task-popup-content"> <div className="task-popup-content">
<p className="task-popup-text-center font-16 pb20">本实训的开启时间{this.state.shixunsmessage} <br/>开启时间之前不能挑战 </p> <p className="task-popup-text-center font-16 pb20">目前该实训项目尚在内测中将于{this.state.shixunsmessage}之后开放谢谢</p>
</div> </div>
<div className="task-popup-submit clearfix"> <div className="task-popup-submit clearfix">
{/*<a onClick={this.hidestartshixunCombattype} className="task-btn fl">取消</a>*/} {/*<a onClick={this.hidestartshixunCombattype} className="task-btn fl">取消</a>*/}

@ -731,8 +731,7 @@ class TPMBanner extends Component {
footer={null} footer={null}
> >
<div className="task-popup-content"> <div className="task-popup-content">
<p className="task-popup-text-center font-16 pb20">本实训的开启时间{shixunsmessage} <br/>开启时间之前不能挑战 <p className="task-popup-text-center font-16 pb20">目前该实训项目尚在内测中将于{shixunsmessage}之后开放谢谢</p>
</p>
</div> </div>
<div className="task-popup-submit clearfix"> <div className="task-popup-submit clearfix">
{/*<a onClick={this.hidestartshixunCombattype} className="task-btn fl">取消</a>*/} {/*<a onClick={this.hidestartshixunCombattype} className="task-btn fl">取消</a>*/}

@ -5,9 +5,9 @@ import PropTypes from 'prop-types';
import NewHeader from './NewHeader' import NewHeader from './NewHeader'
import NewFooter from './NewFooter' import NewFooter from './NewFooter'
import SiderBar from './SiderBar' import SiderBar from './SiderBar'
import { getUrl } from 'educoder' import { getUrl, downloadFile } from 'educoder'
import axios from 'axios'; import axios from 'axios';
import { Spin } from 'antd'
import './TPMIndex.css' import './TPMIndex.css'
import LoginDialog from '../login/LoginDialog'; import LoginDialog from '../login/LoginDialog';
import AccountProfile from '../user/AccountProfile'; import AccountProfile from '../user/AccountProfile';
@ -79,7 +79,9 @@ export function TPMIndexHOC(WrappedComponent) {
coursedata: {}, coursedata: {},
isRender: false, 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() { componentDidMount() {
window.addEventListener('keyup', this.keyupListener)
if(this.props.match.path==="/"){ if(this.props.match.path==="/"){
document.title="创新源于实践"; document.title="创新源于实践";
}else if(this.props.match.path==="/403"){ }else if(this.props.match.path==="/403"){
@ -364,6 +377,29 @@ export function TPMIndexHOC(WrappedComponent) {
DownloadOpenPdf=(type,url)=>{ DownloadOpenPdf=(type,url)=>{
type===true?window.open(url):window.location.href=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() { render() {
let{Headertop,Footerdown, isRender, AccountProfiletype}=this.state; let{Headertop,Footerdown, isRender, AccountProfiletype}=this.state;
const common = { const common = {
@ -387,10 +423,15 @@ export function TPMIndexHOC(WrappedComponent) {
ShowOnlinePdf:(url)=>this.ShowOnlinePdf(url), ShowOnlinePdf:(url)=>this.ShowOnlinePdf(url),
DownloadFileA:(title,url)=>this.DownloadFileA(title,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 ( return (
<div> <div className="indexHOC">
{isRender===true ? <LoginDialog {isRender===true ? <LoginDialog
Modifyloginvalue={()=>this.hideLoginDialog()} Modifyloginvalue={()=>this.hideLoginDialog()}
{...this.props} {...this.props}
@ -423,8 +464,28 @@ export function TPMIndexHOC(WrappedComponent) {
-moz-box-shadow: 0px 0px 12px rgba(0,0,0,0.1); -moz-box-shadow: 0px 0px 12px rgba(0,0,0,0.1);
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> }</style>
<Spin spinning={this.state.globalLoading} delay={0} className="globalSpin"
size="large"
tip= {this._gLoadingTip || "加载中..."}
>
<NewHeader {...this.state} {...this.props}></NewHeader> <NewHeader {...this.state} {...this.props}></NewHeader>
<div className="newContainer newContainers"> <div className="newContainer newContainers">
<WrappedComponent initCommonState={(user)=>this.initCommonState(user)} <WrappedComponent initCommonState={(user)=>this.initCommonState(user)}
@ -440,6 +501,7 @@ export function TPMIndexHOC(WrappedComponent) {
<NewFooter <NewFooter
Footerdown={Footerdown} Footerdown={Footerdown}
/> />
</Spin>
</div> </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 {BrowserRouter as Router, Route, Link, Switch} from "react-router-dom";
// import "antd/dist/antd.css";
import axios from 'axios'; import axios from 'axios';
import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor';
import {getUrl} from 'educoder'; 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 { export default class TPMUpdatepropaede extends Component {
constructor(props) { constructor(props) {
super(props) super(props)
this.neweditanswerRef=React.createRef();
this.state = { 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() { componentDidMount() {
let id = this.props.match.params.shixunId; let id = this.props.match.params.shixunId;
let url="/shixuns/"+id+"/propaedeutics.json"; let url="/shixuns/"+id+"/propaedeutics.json";
axios.get(url).then((response) => { axios.get(url).then((response) => {
console.log(response)
if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { if (response.data.status === 403||response.data.status === 401||response.data.status === 500) {
}else{ }else{
this.setState({ this.setState({
shixunId:id shixunId:id,
}) })
if(response.data.content[0]!=null){ 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{ }else{
this.updatepropaedeMD("", "UpdatepropaedeMD"); this.setState({
editanswersRefval:"",
})
this.neweditanswerRef.current.setValue('')
} }
} }
}).catch((error) => { }).catch((error) => {
@ -148,7 +52,7 @@ export default class TPMUpdatepropaede extends Component {
updatepropaedeuticsvalue=()=>{ updatepropaedeuticsvalue=()=>{
let id = this.props.match.params.shixunId; let id = this.props.match.params.shixunId;
let url="/shixuns/"+id+"/update_propaedeutics.json"; 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,{ axios.post(url,{
content:update_propaedeuticsvalue content:update_propaedeuticsvalue
} }
@ -163,10 +67,7 @@ export default class TPMUpdatepropaede extends Component {
}); });
} }
render() { render() {
let {shixunId} = this.state; let {shixunId} = this.state;
return ( return (
<React.Fragment> <React.Fragment>
<div className="educontent"> <div className="educontent">
@ -178,11 +79,8 @@ export default class TPMUpdatepropaede extends Component {
</div> </div>
<div className="padding40-20"> <div className="padding40-20">
<div className="padding10-20 edu-back-greyf5 radius4" id="UpdatepropaedeMD"> <TPMMDEditor ref={this.neweditanswerRef} placeholder="请输入选择题的题干内容" mdID={'editquestioMDid'} refreshTimeout={1500}
<textarea style={{display: 'none'}} id="Updatepropaedes" name="content"> </textarea> needRecreate={true} watch={true} className="courseMessageMD" initValue={this.neweditanswerRefval}></TPMMDEditor>
<div className="CodeMirror cm-s-defualt">
</div>
</div>
</div> </div>
</div> </div>

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

@ -587,7 +587,7 @@ class Challenges extends Component {
footer={null} footer={null}
> >
<div className="task-popup-content"> <div className="task-popup-content">
<p className="task-popup-text-center font-16 pb20">本实训的开启时间{shixunsmessage} <br/>开启时间之前不能挑战 </p> <p className="task-popup-text-center font-16 pb20">目前该实训项目尚在内测中将于{shixunsmessage}之后开放谢谢 </p>
</div> </div>
<div className="task-popup-submit clearfix"> <div className="task-popup-submit clearfix">
{/*<a onClick={this.hidestartshixunCombattype} className="task-btn fl">取消</a>*/} {/*<a onClick={this.hidestartshixunCombattype} className="task-btn fl">取消</a>*/}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save