dev_aliyun_beta
杨树林 5 years ago
commit 13105efe89

@ -16,6 +16,5 @@ $(document).on('turbolinks:load', function() {
$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
});
}
});

@ -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', '400px');
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', '400px');
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

@ -8,38 +8,12 @@ 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
@ -103,4 +77,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 #全部的答案数组序号
@ -680,8 +680,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

@ -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 毕设任务

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

@ -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="task-hide pl20 edu-txt-left" 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: 220px" 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>已发布实训作业</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="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,3 +1,4 @@
if @bank.container_type == "Exercise"
json.exercise do
json.extract! @bank, :id, :name, :description, :is_public
end
@ -14,3 +15,22 @@ json.exercise_questions do
edit_type:nil
end
end
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.questions do
json.array! @exercise_questions do | question|
json.partial! "exercise_banks/poll_questions", question: question, answers: question.exercise_bank_choices
end
end
end

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

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

@ -848,6 +848,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

@ -37847,7 +37847,6 @@ $(document).on('turbolinks:load', function() {
$searchFrom.find('select[name="status"]').val('pending');
}
});
}
})
;
@ -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
});
}
});
$(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

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

@ -320,10 +320,13 @@ class CommonWorkPost extends Component{
// ModalSave: ()=>this.deleteAttachment(file),
// ModalCancel:this.cancelAttachment
// })
if(file.response!=undefined){
this.deleteAttachment(file)
return false;
}
}
cancelAttachment=()=>{
this.setState({
Modalstype:false,

@ -3,11 +3,11 @@ import { Input, InputNumber, Form, Button, Checkbox, Upload, Icon, message, Moda
import axios from 'axios'
import '../css/busyWork.css'
import '../css/Courses.css'
import { WordsBtn, getUrl, ConditionToolTip } from 'educoder'
import { WordsBtn, getUrl, ConditionToolTip, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll } from 'educoder'
import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor';
import CBreadcrumb from '../common/CBreadcrumb'
import NewWorkForm from './NewWorkForm'
const confirm = Modal.confirm;
const $ = window.$
const MAX_TITLE_LENGTH = 60;
class NewWork extends Component{
@ -17,6 +17,15 @@ class NewWork extends Component{
this.answerMdRef = React.createRef();
this.state={
title_value:"",
title_num: 0,
contentFileList: [],
answerFileList: [],
workLoaded: false,
base_on_project: true,
category: {},
min_num: 2,
max_num: 10,
}
}
componentDidMount () {
@ -41,6 +50,7 @@ class NewWork extends Component{
course_id: data.course_id,
course_name: data.course_name,
category: data.category,
})
}
})
@ -55,13 +65,56 @@ class NewWork extends Component{
.then((response) => {
if (response.data.name) {
const data = response.data;
data.isEdit = true;
const contentFileList = data.attachments.map(item => {
return {
id: item.id,
uid: item.id,
name: appendFileSizeToUploadFile(item),
url: item.url,
filesize: item.filesize,
status: 'done'
}
})
const answerFileList = data.ref_attachments.map(item => {
return {
id: item.id,
uid: item.id,
name: appendFileSizeToUploadFile(item),
url: item.url,
filesize: item.filesize,
status: 'done'
}
})
this.setState({
category: data.category,
course_id: data.course_id,
course_name: data.course_name,
...data,
// course_id: data.course_id,
// course_name: data.course_name,
// category: data.category,
title_num: parseInt(data.name.length),
workLoaded: true,
init_min_num: data.min_num,
init_max_num: data.max_num,
// description: data.description,
reference_answer: data.reference_answer,
contentFileList,
answerFileList,
}, () => {
setTimeout(() => {
this.contentMdRef.current.setValue(data.description || '')
this.answerMdRef.current.setValue(data.reference_answer || '')
}, 2000)
this.props.form.setFieldsValue({
title: data.name,
description: data.description || '',
reference_answer: data.reference_answer || '',
});
})
this.newWorkFormRef.initValue(data);
}
})
.catch(function (error) {
@ -69,12 +122,72 @@ class NewWork extends Component{
});
}
doEdit = (params) => {
// 输入title
changeTitle=(e)=>{
console.log(e.target.value.length);
this.setState({
title_num: parseInt(e.target.value.length)
})
}
handleSubmit = () => {
const courseId = this.state.course_id || this.props.match.params.coursesId ;
this.props.form.validateFieldsAndScroll((err, values) => {
console.log(values)
const mdContnet = this.contentMdRef.current.getValue().trim();
console.log(mdContnet)
values.description = mdContnet;
// return;
{/* max={has_commit ? init_min_num : null } */}
{/* min={has_commit ? init_max_num : (min_num == undefined ? 2 : min_num + 1) } */}
// 已有提交作品,人数范围只能扩大
const { has_commit, max_num, init_max_num, min_num, init_min_num } = this.state;
if (has_commit) {
if (max_num < init_max_num || min_num > init_min_num) {
this.props.showNotification(`已有提交作品,人数范围只能扩大(原设置为:${init_min_num} - ${init_max_num})`)
return;
}
}
// const errKeys = Object.keys(err); // || errKeys.length == 1 && errKeys[0] == 'content' && mdContnet
if (!err) {
if (this.isEdit) {
this.doEdit(courseId, values)
} else {
this.doNew(courseId, values)
}
} else {
$("html").animate({ scrollTop: $('html').scrollTop() - 100 })
}
})
}
doEdit = (courseId, values) => {
const workId = this.props.match.params.workId
const newUrl = `/homework_commons/${workId}.json`
let attachment_ids = this.state.contentFileList.map(item => {
return item.response ? item.response.id : item.id
})
let reference_attachment_ids = this.state.answerFileList.map(item => {
return item.response ? item.response.id : item.id
})
const { min_num, max_num, base_on_project, category } = this.state
const isGroup = this.props.isGroup()
axios.put(newUrl, params)
axios.put(newUrl, {
type: isGroup ? 3 : 1,
name: values.title,
description: values.description,
reference_answer: values.reference_answer,
attachment_ids,
reference_attachment_ids,
min_num,
max_num,
base_on_project
})
.then((response) => {
if (response.data.status == 0) {
this.props.showNotification('保存成功')
@ -85,11 +198,30 @@ class NewWork extends Component{
console.log(error);
});
}
doNew = (params) => {
const courseId = this.props.match.params.coursesId ;
doNew = (courseId, values) => {
const newUrl = `/courses/${courseId}/homework_commons.json`
axios.post(newUrl, params)
let attachment_ids = this.state.contentFileList.map(item => {
return item.response ? item.response.id : item.id
})
let reference_attachment_ids = this.state.answerFileList.map(item => {
return item.response ? item.response.id : item.id
})
const isGroup = this.props.isGroup()
const { min_num, max_num, base_on_project, category } = this.state
axios.post(newUrl, {
type: isGroup ? 3 : 1,
name: values.title,
description: values.description,
reference_answer: values.reference_answer,
attachment_ids,
reference_attachment_ids,
min_num,
max_num,
base_on_project
})
.then((response) => {
if (response.data.status == 0) {
this.props.showNotification('保存成功')
@ -101,25 +233,146 @@ class NewWork extends Component{
});
}
handleContentUploadChange = (info) => {
let contentFileList = info.fileList;
this.setState({ contentFileList: appendFileSizeToUploadFileAll(contentFileList) });
}
handleAnswerUploadChange = (info) => {
let answerFileList = info.fileList;
this.setState({ answerFileList: appendFileSizeToUploadFileAll(answerFileList) });
}
onAttachmentRemove = (file, stateName) => {
if(file.response!=undefined){
this.props.confirm({
content: '是否确认删除?',
onOk: () => {
this.deleteAttachment(file, stateName)
},
onCancel() {
console.log('Cancel');
},
});
return false;
}
}
deleteAttachment = (file, stateName) => {
// 初次上传不能直接取uid
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
axios.delete(url, {
})
.then((response) => {
if (response.data) {
const { status } = response.data;
if (status == 0) {
console.log('--- success')
this.setState((state) => {
const index = state[stateName].indexOf(file);
const newFileList = state[stateName].slice();
newFileList.splice(index, 1);
return {
[stateName]: newFileList,
};
});
}
}
})
.catch(function (error) {
console.log(error);
});
}
max_num_change = (val) => {
if (val < 2) {
this.setState({
max_num: 2,
})
return;
}
const { min_num } = this.state;
this.setState({
max_num: val,
min_num: val <= min_num ? val - 1 : min_num
})
}
min_num_change = (val) => {
this.setState({ min_num: val })
}
base_on_project_change = () => {
this.setState({ base_on_project: !this.state.base_on_project })
}
render(){
let {typeId,coursesId,pageType}=this.props.match.params;
const { getFieldDecorator } = this.props.form;
const isGroup = this.props.isGroup()
const moduleName = !isGroup? "普通作业":"分组作业";
const moduleEngName = this.props.getModuleName()
let{
category
title_value, contentFileList, answerFileList, max_num, min_num, base_on_project,
init_max_num, init_min_num,
title_num, course_name, category, has_commit, has_project
}=this.state
const { current_user } = this.props
const courseId = this.props.match.params.coursesId ;
const courseId = this.state.course_id || this.props.match.params.coursesId ;
const isEdit = this.isEdit;
if ((isEdit == undefined || isEdit) && !this.state.workLoaded) {
return ''
}
const uploadProps = {
width: 600,
fileList: contentFileList,
multiple: true,
// https://github.com/ant-design/ant-design/issues/15505
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
// showUploadList: false,
action: `${getUrl()}/api/attachments.json`,
onChange: this.handleContentUploadChange,
onRemove: (file) => this.onAttachmentRemove(file, 'contentFileList'),
beforeUpload: (file) => {
console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 < 150;
if (!isLt150M) {
message.error('文件大小必须小于150MB!');
}
return isLt150M;
},
};
const answerUploadProps = {
width: 600,
fileList: answerFileList,
multiple: true,
// https://github.com/ant-design/ant-design/issues/15505
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
// showUploadList: false,
action: `${getUrl()}/api/attachments.json`,
onChange: this.handleAnswerUploadChange,
onRemove: (file) => this.onAttachmentRemove(file, 'answerFileList'),
beforeUpload: (file) => {
console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 < 150;
if (!isLt150M) {
message.error('文件大小必须小于150MB!');
}
return isLt150M;
},
};
return(
<div className="newMain">
<div className="educontent mt20 mb50">
{/* <p className="clearfix">
<WordsBtn style="grey" className="fl">{course_name}</WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<WordsBtn style="grey" className="fl">{typeId==1 ?"普通作业":"分组作业"}</WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<span>{pageType==="new"?"新建":"编辑"}</span>
</p> */}
<CBreadcrumb items={[
{ to: current_user && current_user.first_category_url, name: this.state.course_name},
{ to: `/courses/${courseId}/${moduleEngName}/${category && category.category_id ? category.category_id : ''}`
@ -138,16 +391,161 @@ class NewWork extends Component{
</a>
</p>
<div>
<NewWorkForm wrappedComponentRef={(ref) => {this.newWorkFormRef = ref}}
{...this.props}
onSave={this.onSave}
doNew={this.doNew}
doEdit={this.doEdit}
></NewWorkForm>
{/* onSubmit={this.handleSubmit} */}
<style>
{
`
.yslnewworkinputaddonAfter .ant-input{
border-right: none !important;
height: 40px !important;
}
`
}
</style>
<Form className="courseForm">
<Form.Item
label="标题"
className="AboutInputForm"
>
{getFieldDecorator('title', {
rules: [{
required: true, message: '请输入标题'
}],
})(
<Input placeholder="请输入作业标题最大限制60个字符" onInput={this.changeTitle} className="searchView yslnewworkinputaddonAfter searchViewAfter" style={{"width":"100%"}} maxLength={MAX_TITLE_LENGTH} addonAfter={`${String(title_num)}/${MAX_TITLE_LENGTH}`}/>
)}
</Form.Item>
<style>{`
.uploadBtn.ant-btn {
border: none;
color: #4CACFF;
box-shadow: none;
background: transparent;
padding: 0 6px;
}
.ant-upload-list-item:hover .ant-upload-list-item-info{
background-color:#fff;
}
.upload_1 .ant-upload-list {
width: 350px;
}
.ant-input-number {
height: 40px;
line-height: 40px;
}
.workContent.AboutInputForm.ant-form-item {
border-bottom: none;
padding-bottom: 0px !important;
}
.newWorkUpload {
padding: 0px 30px 30px 30px!important;
background: #fff;
width: 100%;
display: inline-block;
border-bottom: 1px solid #EDEDED;
}
`}</style>
{ <Form.Item
label="内容"
className="AboutInputForm workContent mdInForm"
>
{getFieldDecorator('description', {
rules: [{
required: true, message: '请输入作业内容和要求'
}],
})(
<TPMMDEditor ref={this.contentMdRef} placeholder="请在此输入作业内容和要求,最大限制5000个字符" mdID={'courseContentMD'} refreshTimeout={1500}
className="courseMessageMD" initValue={this.state.description}></TPMMDEditor>
)}
</Form.Item> }
<Upload {...uploadProps} className="upload_1 newWorkUpload">
<Button className="uploadBtn">
<Icon type="upload" /> 上传附件
</Button>
(单个文件150M以内)
</Upload>
{ isGroup &&
<Form.Item
label="分组设置"
className="AboutInputForm"
>
{getFieldDecorator('personNum', {
rules: [{
required: false
// required: true, message: '请输入最小人数和最大人数'
}],
})(
<div>
<p className="clearfix">
<ConditionToolTip condition={has_commit} title={'已有提交作品,人数范围只能扩大'}>
{/* max={has_commit ? init_min_num : null } */}
<InputNumber placeholder="请填写每组最小人数" min={1} className="winput-240-40" value={min_num}
onChange={this.min_num_change} style={{width:'180px'}} />
</ConditionToolTip>
<span className="ml15 mr15">~</span>
{/* min={has_commit ? init_max_num : (min_num == undefined ? 2 : min_num + 1) } */}
<ConditionToolTip condition={has_commit} title={'已有提交作品,人数范围只能扩大'}>
<InputNumber className="winput-240-40" placeholder="请填写每组最大人数" value={max_num} max={10}
onChange={this.max_num_change} style={{width:'180px'}} />
</ConditionToolTip>
<label className="color-grey-9 ml20 font-14">项目管理员角色的成员都可以提交作品提交作品时需要关联同组成员组内成员作品共享</label>
</p>
<p className="mt20">
<ConditionToolTip condition={has_commit || has_project} title={'已有关联项目或作品,不能修改'}>
<Checkbox checked={base_on_project} onChange={this.base_on_project_change}
disabled={has_project || has_commit}
>基于项目实施</Checkbox>
</ConditionToolTip>
<label className="color-grey-9 ml12 font-14">勾选后各小组必须在educoder平台创建项目教师可随时观察平台对各小组最新进展的实时统计</label>
</p>
</div>
)}
</Form.Item>
}
<Form.Item
label="参考答案"
className="AboutInputForm"
style={{"borderBottom":"none"}}
>
{getFieldDecorator('reference_answer', {
rules: [{
required: false
}],
})(
<TPMMDEditor ref={this.answerMdRef} placeholder="请在此输入作业的参考答案,最大限制5000个字符" mdID={'workAnswerMD'}
className="courseMessageMD" refreshTimeout={1500} initValue={this.state.reference_answer || ''}></TPMMDEditor>
)}
<Upload {...answerUploadProps} className="upload_1">
<Button className="uploadBtn">
<Icon type="upload" /> 上传附件
</Button>
(单个文件150M以内)
</Upload>
</Form.Item>
<Form.Item>
<div className="clearfix mt30 mb30">
{/* htmlType="submit" */}
<Button type="primary" onClick={this.handleSubmit} className="defalutSubmitbtn fl mr20">提交</Button>
<a className="defalutCancelbtn fl" onClick={() => this.props.toListPage(this.props.match.params, category.category_id)}>取消</ a>
</div>
</Form.Item>
</Form>
</div>
</div>
</div>
)
}
}
export default NewWork;
const WrappedBoardsNew = Form.create({ name: 'NewWork' })(NewWork);
export default WrappedBoardsNew;

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

@ -73,9 +73,10 @@ class AccessoryModal extends Component{
// ModalCancel:this.cancelAttachment
// })
// return false;
if(file.response!=undefined){
this.deleteAttachment(file);
}
}

@ -64,10 +64,12 @@ class AccessoryModal2 extends Component{
// ModalCancel:this.cancelAttachment
// })
// return false;
if(file.response!=undefined){
this.deleteAttachment(file);
}
}

@ -296,8 +296,7 @@ class Selectsetting extends Component{
onAttachmentRemove = (file) => {
// const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
if(file.response!=undefined){
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
axios.delete(url, {
})
@ -328,6 +327,9 @@ class Selectsetting extends Component{
fileListtype:false,
})
}
// const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
}
onChangeTimepublishs= (date, dateString,key) => {
let {course_groups}=this.state;

@ -132,7 +132,7 @@ class Sendresource extends Component{
onAttachmentRemove = (file) => {
if(file.response!=undefined){
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
axios.delete(url, {
})
@ -155,6 +155,7 @@ class Sendresource extends Component{
fileListtype:false,
})
}
}
ModalCancelModalCancel=()=>{
this.setState({

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

@ -157,6 +157,7 @@ class GraduationTasksSubmitedit extends Component{
}
onAttachmentRemove = (file) => {
if(file.response!=undefined){
let {attachments,fileList}=this.state;
const url = `/attachments/${file}.json`
axios.delete(url, {
@ -202,6 +203,8 @@ class GraduationTasksSubmitedit extends Component{
});
}
}
inputSearchValue=(e)=>{
if(e.target.value===""){

@ -146,7 +146,7 @@ class GraduationTasksSubmitnew extends Component{
// },
// });
// return false;
if(file.response!=undefined){
this.setState({
Modalstype:true,
Modalstopval:'确定要删除这个附件吗?',
@ -156,6 +156,8 @@ class GraduationTasksSubmitnew extends Component{
return false;
}
}
cancelAttachment=()=>{
this.setState({
Modalstype:false,

@ -173,6 +173,7 @@ class GraduationTasksnew extends Component {
}
onAttachmentRemove = (file) => {
if(file.response!=undefined){
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
// const url = `/attachments/${file}.json`
axios.delete(url, {})
@ -199,6 +200,8 @@ class GraduationTasksnew extends Component {
});
}
}
//滚动
ifHasAnchorJustScorll() {
// let anchor = this.getURLStuff("anchor");

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

@ -73,6 +73,7 @@ class CreateGroupByImportModal extends Component{
}
onAttachmentRemove = (file) => {
if(file.response!=undefined){
this.props.confirm({
content: '是否确认删除?',
@ -86,6 +87,8 @@ class CreateGroupByImportModal extends Component{
return false;
}
}
deleteAttachment = (file) => {
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
axios.delete(url, {

@ -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'
@ -151,10 +150,19 @@ class studentsList extends Component{
})
}
}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({
// url: url,
// url: fileUrl,
// successCallback: (url) => {
// console.log('successCallback')
// },
@ -162,6 +170,7 @@ class studentsList extends Component{
// console.log('failCallback')
// }
// })
// window.open(fileUrl, "_self");// , '_blank'
}
}).catch((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)
}).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>
),

@ -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,10 +693,16 @@ 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) {
@ -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)
}
}

@ -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,
slowDownloading: false
}
}
@ -133,7 +135,18 @@ export function TPMIndexHOC(WrappedComponent) {
})
}
keyupListener = (e) => {
if (e.key === "Escape") {
this.setState({ slowDownloading: 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,22 @@ export function TPMIndexHOC(WrappedComponent) {
DownloadOpenPdf=(type,url)=>{
type===true?window.open(url):window.location.href=url;
}
slowDownload = (url, tip) => {
this._slowDownloadTip = tip || '正在生成文件,请稍后...';
this.setState({ slowDownloading: true })
const fileUrl = url;
downloadFile({
url: fileUrl,
successCallback: (url) => {
this.setState({ slowDownloading: false })
console.log('successCallback')
},
failCallback: (responseHtml, url) => {
this.setState({ slowDownloading: false })
console.log('failCallback')
}
})
}
render() {
let{Headertop,Footerdown, isRender, AccountProfiletype}=this.state;
const common = {
@ -387,10 +416,12 @@ 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
}
return (
<div>
<div className="indexHOC">
{isRender===true ? <LoginDialog
Modifyloginvalue={()=>this.hideLoginDialog()}
{...this.props}
@ -423,8 +454,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 {
}
.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.slowDownloading} delay={0} className="globalSpin"
size="large"
tip= {this._slowDownloadTip || "加载中..."}
>
<NewHeader {...this.state} {...this.props}></NewHeader>
<div className="newContainer newContainers">
<WrappedComponent initCommonState={(user)=>this.initCommonState(user)}
@ -440,6 +491,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 {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);
});
import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor';
$("#" + 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");
import {getUrl} from 'educoder';
callback && callback()
}
});
return editorName;
}
export default class TPMUpdatepropaede extends Component {
constructor(props) {
super(props)
this.neweditanswerRef=React.createRef();
this.state = {
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)
shixunId:undefined,
}
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>

@ -1384,6 +1384,7 @@ export default class TPMsettings extends Component {
}
onAttachmentRemove = (file) => {
if(file.response!=undefined){
confirm({
title: '确定要删除这个附件吗?',
okText: '确定',
@ -1400,6 +1401,8 @@ export default class TPMsettings extends Component {
return false;
}
}
deleteAttachment = (file) => {
console.log(file);
let id=file.response ==undefined ? file.id : file.response.id

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

@ -772,6 +772,7 @@ class Newshixuns extends Component {
}
onAttachmentRemove = (file) => {
if(file.response!=undefined){
confirm({
title: '确定要删除这个附件吗?',
okText: '确定',
@ -787,6 +788,8 @@ class Newshixuns extends Component {
});
return false;
}
}
deleteAttachment = (file) => {
console.log(file);
let id=file.response ==undefined ? file.id : file.response.id

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