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

mast_item_banktwo
杨树林 5 years ago
commit 31e18b6ac2

@ -1349,6 +1349,7 @@ class CoursesController < ApplicationController
def search_slim def search_slim
courses = current_user.manage_courses.not_deleted.processing courses = current_user.manage_courses.not_deleted.processing
courses = courses.where(id: current_laboratory.all_courses)
keyword = params[:keyword].to_s.strip keyword = params[:keyword].to_s.strip
if keyword.present? if keyword.present?

@ -26,6 +26,7 @@ class ExerciseAnswersController < ApplicationController
end end
elsif q_type == Exercise::MULTIPLE #多选题的 elsif q_type == Exercise::MULTIPLE #多选题的
choice_ids = params[:exercise_choice_id].present? ? params[:exercise_choice_id] : [] choice_ids = params[:exercise_choice_id].present? ? params[:exercise_choice_id] : []
question_choice_ids = @exercise_question.exercise_choices.pluck(:id)
ea_ids = ea.pluck(:exercise_choice_id) ea_ids = ea.pluck(:exercise_choice_id)
common_answer_ids = choice_ids & ea_ids #已经存在的试卷选项id common_answer_ids = choice_ids & ea_ids #已经存在的试卷选项id
@ -37,7 +38,8 @@ class ExerciseAnswersController < ApplicationController
:user_id => current_user.id, :user_id => current_user.id,
:exercise_question_id => @exercise_question.id, :exercise_question_id => @exercise_question.id,
:exercise_choice_id => e, :exercise_choice_id => e,
:answer_text => "" :answer_text => "",
:choice_index => question_choice_ids.index(e).to_i + 1 # choice的序号
} }
ex_a = ExerciseAnswer.new(answer_option) ex_a = ExerciseAnswer.new(answer_option)
ex_a.save! ex_a.save!
@ -52,7 +54,8 @@ class ExerciseAnswersController < ApplicationController
:user_id => current_user.id, :user_id => current_user.id,
:exercise_question_id => @exercise_question.id, :exercise_question_id => @exercise_question.id,
:exercise_choice_id => choice_id, :exercise_choice_id => choice_id,
:answer_text => answer_text :answer_text => answer_text,
:choice_index => choice_id
} }
ea_answer = ea.search_answer_users("exercise_choice_id",choice_id) ea_answer = ea.search_answer_users("exercise_choice_id",choice_id)
if ea.present? && ea_answer.present? if ea.present? && ea_answer.present?

@ -316,7 +316,8 @@ class ExercisesController < ApplicationController
:question_number => q.question_number, :question_number => q.question_number,
:question_score => q.question_score, :question_score => q.question_score,
:shixun_id => q.shixun_id, :shixun_id => q.shixun_id,
:shixun_name => q.shixun_name :shixun_name => q.shixun_name,
:is_ordered => q.is_ordered
} }
exercise_bank_question = current_ex_bank.exercise_bank_questions.new option exercise_bank_question = current_ex_bank.exercise_bank_questions.new option
exercise_bank_question.save! exercise_bank_question.save!

@ -787,7 +787,8 @@ class HomeworkCommonsController < ApplicationController
def create_shixun_homework def create_shixun_homework
tip_exception("请至少选择一个实训") if params[:shixun_ids].blank? tip_exception("请至少选择一个实训") if params[:shixun_ids].blank?
shixuns = Shixun.where(id: params[:shixun_ids]).reorder("id desc") order_ids = params[:shixun_ids].size > 0 ? params[:shixun_ids].reverse.join(',') : -1
shixuns = Shixun.where(id: params[:shixun_ids]).order("field(id, #{order_ids})")
@homework_ids = [] @homework_ids = []
unless params[:category_id].blank? unless params[:category_id].blank?
@category = @course.course_second_categories.find_by(id: params[:category_id], category_type: "shixun_homework") @category = @course.course_second_categories.find_by(id: params[:category_id], category_type: "shixun_homework")

@ -12,13 +12,7 @@ class ItemBasketsController < ApplicationController
end end
def basket_list def basket_list
@single_questions_count = current_user.item_baskets.where(item_type: "SINGLE").count @basket_count = current_user.item_baskets.group(:item_type).count
@multiple_questions_count = current_user.item_baskets.where(item_type: "MULTIPLE").count
@judgement_questions_count = current_user.item_baskets.where(item_type: "JUDGMENT").count
@completion_questions_count = current_user.item_baskets.where(item_type: "COMPLETION").count
@subjective_questions_count = current_user.item_baskets.where(item_type: "SUBJECTIVE").count
@practical_questions_count = current_user.item_baskets.where(item_type: "PRACTICAL").count
@program_questions_count = current_user.item_baskets.where(item_type: "PROGRAM").count
end end
def create def create

@ -259,7 +259,8 @@ class QuestionBanksController < ApplicationController
:question_number => q.question_number, :question_number => q.question_number,
:question_score => q.question_score, :question_score => q.question_score,
:shixun_name => q.shixun_name, :shixun_name => q.shixun_name,
:shixun_id => q.shixun_id :shixun_id => q.shixun_id,
:is_ordered => q.is_ordered
} }
exercise_question = new_exercise.exercise_questions.new option exercise_question = new_exercise.exercise_questions.new option
# question_type5实训题其他是非实训题 # question_type5实训题其他是非实训题

@ -1,5 +1,9 @@
class ShixunListsController < ApplicationController class ShixunListsController < ApplicationController
def index def index
# 去除开头标点符号
@reg = /^[,。?:;‘’!“”—……、]/
# 附件的替换
@atta_reg = /!\[.*]\(\/api\/attachments\/\d+\)/
@results = ShixunSearchService.call(search_params, current_laboratory) @results = ShixunSearchService.call(search_params, current_laboratory)
end end

@ -291,13 +291,13 @@ class ShixunsController < ApplicationController
new_challenge.attributes = challenge.attributes.dup.except("id","shixun_id","user_id", "challenge_tags_count") new_challenge.attributes = challenge.attributes.dup.except("id","shixun_id","user_id", "challenge_tags_count")
new_challenge.user_id = User.current.id new_challenge.user_id = User.current.id
new_challenge.shixun_id = @new_shixun.id new_challenge.shixun_id = @new_shixun.id
new_challenge.save! new_challenge.save!(validate: false)
# 同步参考答案 # 同步参考答案
challenge.challenge_answers.each do |answer| challenge.challenge_answers.each do |answer|
new_answer = ChallengeAnswer.new new_answer = ChallengeAnswer.new
new_answer.attributes = answer.attributes.dup.except("id","challenge_id") new_answer.attributes = answer.attributes.dup.except("id","challenge_id")
new_answer.challenge_id = new_challenge.id new_answer.challenge_id = new_challenge.id
new_answer.save! new_answer.save!(validate: false)
end end
if challenge.st == 0 # 评测题 if challenge.st == 0 # 评测题
# 同步测试集 # 同步测试集
@ -306,7 +306,7 @@ class ShixunsController < ApplicationController
new_test_set = TestSet.new new_test_set = TestSet.new
new_test_set.attributes = test_set.attributes.dup.except("id","challenge_id") new_test_set.attributes = test_set.attributes.dup.except("id","challenge_id")
new_test_set.challenge_id = new_challenge.id new_test_set.challenge_id = new_challenge.id
new_test_set.save! new_test_set.save!(validate: false)
end end
end end
# 同步关卡标签 # 同步关卡标签
@ -1187,7 +1187,10 @@ private
end end
def validate_wachat_support def validate_wachat_support
tip_exception(-2, "..") if (params[:wechat].present? && !@shixun.is_wechat_support?)
if (params[:wechat].present? && !@shixun.is_wechat_support?)
tip_exception(-5, "..")
end
end end
end end

@ -200,7 +200,8 @@ class SubjectsController < ApplicationController
end end
def append_to_stage def append_to_stage
@shixuns = Shixun.where(id: params[:shixun_id]).order("id desc") order_ids = params[:shixun_id].size > 0 ? params[:shixun_id].join(',') : -1
@shixuns = Shixun.where(id: params[:shixun_id]).order("field(id, #{order_ids})")
end end
# 添加实训项目 # 添加实训项目

@ -21,6 +21,8 @@ class Weapps::CodeSessionsController < Weapps::BaseController
Rails.logger.info("[Weapp] code: #{params[:code]}") Rails.logger.info("[Weapp] code: #{params[:code]}")
user_info = Wechat::Weapp.decrypt(result['session_key'], params[:encrypted_data], params[:iv]) user_info = Wechat::Weapp.decrypt(result['session_key'], params[:encrypted_data], params[:iv])
# user_info.delete(:nickName)
# 老用户,已绑定 # 老用户,已绑定
open_user = OpenUsers::Wechat.find_by(uid: user_info['unionId']) open_user = OpenUsers::Wechat.find_by(uid: user_info['unionId'])
if open_user.present? && open_user.user if open_user.present? && open_user.user
@ -29,7 +31,7 @@ class Weapps::CodeSessionsController < Weapps::BaseController
end end
set_session_unionid(user_info['unionId']) set_session_unionid(user_info['unionId'])
user_info['nickname'] = user_info['nickName'] # user_info['nickname'] = user_info['nickName']
session[:wechat_user_extra] = user_info session[:wechat_user_extra] = user_info
end end

@ -108,7 +108,7 @@ class Weapps::CoursesController < Weapps::BaseController
def change_member_roles def change_member_roles
@course = current_course @course = current_course
tip_exception("请至少选择一个角色") if params[:roles].reject(&:blank?).blank? tip_exception("请至少选择一个角色") if params[:roles].reject(&:blank?).blank?
tip_exception("不能具有老师、助教两种角色") if params[:roles].include?("PROFESSOR") && params[:roles].include?("ASSISTANT_PROFESSOR") tip_exception("教师、助教角色只能二选一") if params[:roles].include?("PROFESSOR") && params[:roles].include?("ASSISTANT_PROFESSOR")
params[:user_ids].each do |user_id| params[:user_ids].each do |user_id|
course_members = @course.course_members.where(user_id: user_id) course_members = @course.course_members.where(user_id: user_id)

@ -16,6 +16,7 @@ class Weapps::HomesController < Weapps::BaseController
current_user.manage_courses current_user.manage_courses
end end
@courses = @courses.not_deleted.not_excellent @courses = @courses.not_deleted.not_excellent
@courses = @courses.where(id: current_laboratory.all_courses)
@course_count = @courses.count @course_count = @courses.count
order_str = "course_members.sticky=1 desc, course_members.sticky_time desc, courses.created_at desc" order_str = "course_members.sticky=1 desc, course_members.sticky_time desc, courses.created_at desc"
@courses = paginate(@courses.order(order_str).includes(:teacher, :school)) @courses = paginate(@courses.order(order_str).includes(:teacher, :school))

@ -15,8 +15,9 @@ class Weapps::SessionsController < Weapps::BaseController
return return
end end
# session[:wechat_user_extra].delete(:nickName)
# 绑定微信号 # 绑定微信号
OpenUsers::Wechat.create!(user: user, uid: session_unionid, extra: session[:wechat_user_extra]) if user.wechat_open_user.blank? OpenUsers::Wechat.create!(user: user, uid: session_unionid) if user.wechat_open_user.blank?
successful_authentication(user) successful_authentication(user)
end end

@ -2,9 +2,11 @@ module ExerciseQuestionsHelper
def get_exercise_question_info(question,exercise,ex_user,ex_answerer_id) def get_exercise_question_info(question,exercise,ex_user,ex_answerer_id)
answered_content = [] answered_content = []
exercise_answers = question.exercise_answers.search_exercise_answer("user_id",ex_answerer_id) #试卷用户的回答 exercise_answers = question.exercise_answers.search_exercise_answer("user_id",ex_answerer_id) #试卷用户的回答
if question.question_type <= 2 if question.question_type == Exercise::SINGLE || question.question_type == Exercise::JUDGMENT
answered_content << exercise_answers.last&.exercise_choice_id if exercise_answers.present?
elsif question.question_type == Exercise::MULTIPLE
answered_content = exercise_answers.pluck(:exercise_choice_id) answered_content = exercise_answers.pluck(:exercise_choice_id)
elsif question.question_type == 3 elsif question.question_type == Exercise::COMPLETION
exercise_answers.each do |a| exercise_answers.each do |a|
u_answer = { u_answer = {
"choice_id": a.exercise_choice_id, "choice_id": a.exercise_choice_id,
@ -12,10 +14,10 @@ module ExerciseQuestionsHelper
} }
answered_content.push(u_answer) answered_content.push(u_answer)
end end
elsif question.question_type == 4 elsif question.question_type == Exercise::SUBJECTIVE
answered_content = exercise_answers.pluck(:answer_text) answered_content = exercise_answers.pluck(:answer_text)
end end
if question.question_type == 5 && ex_user.present? && ex_user.commit_status == 1 #存在实训题,且用户已提交了的,如果实训题只做了一半就关闭,则相当于不要了 if question.question_type == Exercise::PRACTICAL && ex_user.present? && ex_user.commit_status == 1 #存在实训题,且用户已提交了的,如果实训题只做了一半就关闭,则相当于不要了
if exercise.exercise_status == 3 #如果试卷已截止,则可以看到分数,否则不能查看分数 if exercise.exercise_status == 3 #如果试卷已截止,则可以看到分数,否则不能查看分数
shixun_type = 2 shixun_type = 2
else else

@ -16,8 +16,8 @@ module ExercisesHelper
end end
if q_type <= Exercise::JUDGMENT if q_type <= Exercise::JUDGMENT
if answers_content.present? #学生有回答时,分数已经全部存到exercise_answer 表,所以可以直接取第一个值 if answers_content.present? #学生有回答时,分数已经全部存到exercise_answer 表,多选题直接取第一个值,单选题和判断题选最后一个值(考虑并发)
ques_score = answers_content.first.score ques_score = q_type == Exercise::MULTIPLE ? answers_content.first.score : answers_content.last.score
ques_score = ques_score < 0 ? 0.0 : ques_score ques_score = ques_score < 0 ? 0.0 : ques_score
# answer_choice_array = [] # answer_choice_array = []
# answers_content.each do |a| # answers_content.each do |a|
@ -441,6 +441,17 @@ module ExercisesHelper
end end
if q.question_type <= 2 #为选择题或判断题时 if q.question_type <= 2 #为选择题或判断题时
if answers_content.present? #学生有回答时 if answers_content.present? #学生有回答时
if q.question_type == 0 || q.question_type == 2 ## 单选、判断题的算分与多选题分开计算
user_answer = answers_content.last.exercise_choice.choice_position
standard_answer = q.exercise_standard_answers.first&.exercise_choice_id
if standard_answer.present? && user_answer == standard_answer
q_score_1 = q.question_score
score1 = score1 + q.question_score
else
q_score_1 = -1.0
end
answers_content.last.update!(score: q_score_1)
else
answer_choice_array = [] answer_choice_array = []
answers_content.each do |a| answers_content.each do |a|
answer_choice_array.push(a.exercise_choice.choice_position) #学生答案的位置 answer_choice_array.push(a.exercise_choice.choice_position) #学生答案的位置
@ -466,6 +477,7 @@ module ExercisesHelper
answers_content.update_all(:score => -1.0) answers_content.update_all(:score => -1.0)
score1 += 0.0 score1 += 0.0
end end
end
else else
score1 += 0.0 score1 += 0.0
end end

@ -1,12 +1,12 @@
module ShixunsHelper module ShixunsHelper
def level_to_s(level) def level_to_s(level)
%W(初级 中级 高级 顶)[level-1] %W(初级 中级 中高级 高)[level-1]
end end
#难度 #难度
def diff_to_s(trainee) def diff_to_s(trainee)
%W(初级学员 中级学员 高级学员 顶级学员)[trainee-1] %W(初级学员 中级学员 中高级学员 高级学员)[trainee-1]
end end
#1. 未发布 #1. 未发布
@ -24,6 +24,8 @@ module ShixunsHelper
"已发布" "已发布"
when 3 when 3
"已关闭" "已关闭"
when -1
"已删除"
end end
end end

@ -19,38 +19,26 @@ class ItemBank < ApplicationRecord
end end
def apply? def apply?
!public && ApplyAction.where(container_type: "ItemBank", container_id: id, status: 0).exists? !public && ApplyAction.exists?(container_type: "ItemBank", container_id: id, status: 0)
end end
def type_string def type_string
result = case item_type case item_type
when "SINGLE" when "SINGLE" then "单选题"
"单选题" when "MULTIPLE" then "多选题"
when "MULTIPLE" when "JUDGMENT" then "判断题"
"多选题" when "COMPLETION" then "填空题"
when "JUDGMENT" when "SUBJECTIVE" then "简答题"
"判断题" when "PRACTICAL" then "实训题"
when "COMPLETION" when "PROGRAM" then "编程题"
"填空题"
when "SUBJECTIVE"
"简答题"
when "PRACTICAL"
"实训题"
when "PROGRAM"
"编程题"
end end
result
end end
def difficulty_string def difficulty_string
result = case difficulty case difficulty
when 1 when 1 then "简单"
"简单" when 2 then "适中"
when 2 when 3 then "困难"
"适中"
when 3
"困难"
end end
result
end end
end end

@ -276,8 +276,8 @@ class Shixun < ApplicationRecord
case trainee case trainee
when 1 then '初级学员' when 1 then '初级学员'
when 2 then '中级学员' when 2 then '中级学员'
when 3 then '高级学员' when 3 then '高级学员'
when 4 then '级学员' when 4 then '级学员'
else '' else ''
end end
end end
@ -286,8 +286,8 @@ class Shixun < ApplicationRecord
case trainee case trainee
when 1 then '初级' when 1 then '初级'
when 2 then '中级' when 2 then '中级'
when 3 then '高级' when 3 then '高级'
when 4 then '级' when 4 then '级'
else '' else ''
end end
end end

@ -223,7 +223,7 @@ class StudentWork < ApplicationRecord
game_score = adjust_score.score game_score = adjust_score.score
elsif game.present? elsif game.present?
setting = homework_common.homework_group_setting game.user_id setting = homework_common.homework_group_setting game.user_id
if game.status == 2 && ((game.end_time && game.end_time < setting.end_time) || (homework_common.allow_late && game.end_time && game.end_time < homework_common.late_time)) if game.status == 2 && ((game.end_time && setting.end_time && game.end_time < setting.end_time) || (homework_common.allow_late && homework_common.late_time && game.end_time && game.end_time < homework_common.late_time))
answer_open_evaluation = homework_common.homework_detail_manual.answer_open_evaluation answer_open_evaluation = homework_common.homework_detail_manual.answer_open_evaluation
game_score = answer_open_evaluation ? score : (game.final_score > 0 ? game.real_score(score) : 0) game_score = answer_open_evaluation ? score : (game.final_score > 0 ? game.real_score(score) : 0)
end end

@ -51,6 +51,7 @@ class Admins::ShixunSettingsQuery < ApplicationQuery
all_shixuns = all_shixuns.where(task_pass: params[:task_pass]) if params[:task_pass] all_shixuns = all_shixuns.where(task_pass: params[:task_pass]) if params[:task_pass]
all_shixuns = all_shixuns.where(code_hidden: params[:code_hidden]) if params[:code_hidden] all_shixuns = all_shixuns.where(code_hidden: params[:code_hidden]) if params[:code_hidden]
all_shixuns = all_shixuns.where(vip: params[:vip]) if params[:vip] all_shixuns = all_shixuns.where(vip: params[:vip]) if params[:vip]
all_shixuns = all_shixuns.where(is_wechat_support: params[:is_wechat_support]) if params[:is_wechat_support]
custom_sort(all_shixuns, params[:sort_by], params[:sort_direction]) custom_sort(all_shixuns, params[:sort_by], params[:sort_direction])
end end

@ -68,8 +68,8 @@ class GitService
content = JSON.parse(body) content = JSON.parse(body)
if content["code"] != 0 if content["code"] != 0
raise("版本库异常")
logger.error("repository error: #{content['msg']}") logger.error("repository error: #{content['msg']}")
raise("版本库异常")
end end
#raise content["msg"] if content["code"] != 0 #raise content["msg"] if content["code"] != 0

@ -26,7 +26,7 @@ class ShixunSearchService < ApplicationService
shixun_ids = ShixunSchool.where(school_id: User.current.school_id).pluck(:shixun_id) shixun_ids = ShixunSchool.where(school_id: User.current.school_id).pluck(:shixun_id)
shixun_ids = shixun_ids.reject(&:blank?).length == 0 ? -1 : shixun_ids.join(",") shixun_ids = shixun_ids.reject(&:blank?).length == 0 ? -1 : shixun_ids.join(",")
@shixuns = @shixuns.where("use_scope = 0 or id in (#{shixun_ids})").unhidden.published.or(@shixuns.where(id: User.current.shixuns)) @shixuns = @shixuns.where("use_scope = 0 or id in (#{shixun_ids})").unhidden.publiced.or(@shixuns.where(id: User.current.shixuns))
end end
end end
@ -43,7 +43,6 @@ class ShixunSearchService < ApplicationService
@shixuns = @shixuns.where(trainee: params[:diff]) @shixuns = @shixuns.where(trainee: params[:diff])
end end
Rails.logger.info("search_shixun_ids: #{@shixuns.pluck(:id)}")
Shixun.search(keyword, search_options) Shixun.search(keyword, search_options)
end end
@ -53,7 +52,7 @@ class ShixunSearchService < ApplicationService
order = order =
if sort_str == "wechat_myshixuns_count" if sort_str == "wechat_myshixuns_count"
{"is_wechat_support" => "desc", sort_str => order_str} {"is_wechat_support" => "desc", "myshixuns_count" => order_str}
else else
{sort_str => order_str} {sort_str => order_str}
end end

@ -10,7 +10,8 @@ class Subjects::ShixunUsedInfoService < ApplicationService
stages.each do |stage| stages.each do |stage|
position = stage.position position = stage.position
shixuns = stage.shixuns.includes(myshixuns: :games, homework_commons: :course) shixuns = stage.shixuns.includes(myshixuns: :games, homework_commons: :course)
shixuns.each_with_index do |shixun, index| shixuns.find_in_batches(batch_size: 1000) do |s|
Parallel.each_with_index(s, in_processes: 2) do |shixun, index|
stage = "#{position}-#{index+1}" stage = "#{position}-#{index+1}"
name = shixun.name name = shixun.name
myshixuns = shixun.myshixuns myshixuns = shixun.myshixuns
@ -24,6 +25,8 @@ class Subjects::ShixunUsedInfoService < ApplicationService
shixun_infos << {stage: stage, name: name, challenge_count: challenge_count, course_count: course_count, shixun_infos << {stage: stage, name: name, challenge_count: challenge_count, course_count: course_count,
school_count: school_count, used_count: used_count, passed_count: passed_count, school_count: school_count, used_count: used_count, passed_count: passed_count,
evaluate_count: evaluate_count, passed_ave_time: passed_ave_time, shixun_id: shixun.id} evaluate_count: evaluate_count, passed_ave_time: passed_ave_time, shixun_id: shixun.id}
end
end end
end end
shixun_infos shixun_infos

@ -13,7 +13,7 @@ class Subjects::UserUsedInfoService < ApplicationService
users_info = [] users_info = []
users = User.includes(myshixuns: :games).where(myshixuns: {shixun_id: shixun_ids}, games: {status: 2}, users: {is_test: false}) users = User.includes(myshixuns: :games).where(myshixuns: {shixun_id: shixun_ids}, games: {status: 2}, users: {is_test: false})
users.find_in_batches(batch_size: 500) do |u| users.find_in_batches(batch_size: 500) do |u|
Parallel.each(u, in_processes: 5) do |user| Parallel.each(u, in_processes: 2) do |user|
myshixuns = user.myshixuns.select{|m| shixun_ids.include?(m.shixun_id)} myshixuns = user.myshixuns.select{|m| shixun_ids.include?(m.shixun_id)}
name = "#{user.lastname}#{user.firstname}" name = "#{user.lastname}#{user.firstname}"
passed_myshixun_count = myshixuns.select{|m| m.status == 1}.size passed_myshixun_count = myshixuns.select{|m| m.status == 1}.size

@ -16,7 +16,7 @@
<% identifier = Game.find_by(challenge_id: discuss.challenge_id, user_id: discuss.user_id)&.identifier %> <% identifier = Game.find_by(challenge_id: discuss.challenge_id, user_id: discuss.user_id)&.identifier %>
<td class="text-left"><%= link_to discuss.dis.name, "/tasks/#{identifier}", target: '_blank'%></td> <td class="text-left"><%= link_to discuss.dis.name, "/tasks/#{identifier}", target: '_blank'%></td>
<td class="text-left content-img"><%= content_safe discuss.content %></td> <td class="text-left content-img"><%= content_safe discuss.content %></td>
<td><%= discuss.user.show_real_name %></td> <td><%= link_to discuss.user.show_real_name, "/users/#{discuss.user.login}", target: '_blank' %></td>
<td><%= format_time discuss.created_at %></td> <td><%= format_time discuss.created_at %></td>
</tr> </tr>
<% end %> <% end %>

@ -71,6 +71,13 @@
<span class="only_view">只看vip</span> <span class="only_view">只看vip</span>
</label> </label>
</div> </div>
<div class="mr-5">
<label for="is_wechat_support">
<%= check_box_tag :is_wechat_support, !@sort_json[:is_wechat_support],@sort_json[:is_wechat_support], class:"shixun-settings-select" %>
<span class="only_view">只看小程序可用</span>
</label>
</div>
</div> </div>
</div> </div>
<% end %> <% end %>

@ -3,4 +3,5 @@ json.game @game
json.shixun @shixun json.shixun @shixun
json.shixun_env @env json.shixun_env @env
json.shixun_image @shixun.main_mirror_name
json.shixun_tags @shixun_tags json.shixun_tags @shixun_tags

@ -1,7 +1,7 @@
json.single_questions_count @single_questions_count json.single_questions_count @basket_count&.fetch("SINGLE", 0)
json.multiple_questions_count @multiple_questions_count json.multiple_questions_count @basket_count&.fetch("MULTIPLE", 0)
json.judgement_questions_count @judgement_questions_count json.judgement_questions_count @basket_count&.fetch("JUDGMENT", 0)
json.completion_questions_count @completion_questions_count json.completion_questions_count @basket_count&.fetch("COMPLETION", 0)
json.subjective_questions_count @subjective_questions_count json.subjective_questions_count @basket_count&.fetch("SUBJECTIVE", 0)
json.practical_questions_count @practical_questions_count json.practical_questions_count @basket_count&.fetch("PRACTICAL", 0)
json.program_questions_count @program_questions_count json.program_questions_count @basket_count&.fetch("PROGRAM", 0)

@ -4,25 +4,21 @@ json.shixun_list do
json.array! @results.with_highlights(multiple: true) do |obj, highlights| json.array! @results.with_highlights(multiple: true) do |obj, highlights|
json.merge! obj.to_searchable_json json.merge! obj.to_searchable_json
json.challenge_names obj.challenges.pluck(:subject) json.challenge_names obj.challenges.pluck(:subject)
highlights[:description]&.first&.sub!(@reg, '')
# 去除开头标点符号 highlights[:description]&.map{|des| des.gsub(@atta_reg, '')}
reg = /^[,。?:;‘’!“”—……、]/ highlights[:content]&.first&.sub!(@reg, '')
# 附件的替换 highlights[:content]&.map{|des| des.gsub(@atta_reg, '')}
atta_reg = /!\[.*]\(\/api\/attachments\/\d+\)/
highlights[:description]&.first&.sub!(reg, '')
highlights[:description]&.map{|des| des.gsub(atta_reg, '')}
highlights[:content]&.first&.sub!(reg, '')
highlights[:content]&.map{|des| des.gsub(atta_reg, '')}
json.title highlights.delete(:name)&.join('...') || obj.searchable_title json.title highlights.delete(:name)&.join('...') || obj.searchable_title
json.description highlights[:description]&.join('...') || Util.extract_content(obj.description)[0..300]&.gsub(atta_reg, '') json.description highlights[:description]&.join('...') || Util.extract_content(obj.description)[0..300]&.gsub(@atta_reg, '')
json.pic url_to_avatar(obj) json.pic url_to_avatar(obj)
json.content highlights json.content highlights
json.level level_to_s(obj.trainee) json.level level_to_s(obj.trainee)
json.subjects obj.subjects.visible.unhidden.uniq do |subject| #if params[:sort] != "wechat_myshixuns_count"
json.subjects obj.subjects.select{ |s| s.status == 2 && s.hidden == 0} do |subject|
json.(subject, :id, :name) json.(subject, :id, :name)
end end
#end
end end
end end

@ -0,0 +1,17 @@
class AddChoiceIndexToExerciseAnswers < ActiveRecord::Migration[5.2]
def change
add_column :exercise_answers, :choice_index, :integer, default: 1
multi_questions = ExerciseQuestion.where(question_type: 1)
multi_questions.includes(:exercise_choices, :exercise_answers).find_each do |question|
exercise_answers = question.exercise_answers
exercise_answers.find_each do |answer|
choice_index = question.exercise_choices.pluck(:id).index(answer.exercise_choice_id).to_i + 1
answer.update_column('choice_index', choice_index)
end
puts "multi_questions: #{question.id}"
end
ExerciseAnswer.joins(:exercise_question).where(exercise_questions: {question_type: 3}).update_all("choice_index = exercise_choice_id")
end
end

@ -0,0 +1,13 @@
class AddChoiceIndexUniqIndexToExerciseAnswers < ActiveRecord::Migration[5.2]
def change
sql = %Q(delete from exercise_answers where (exercise_question_id, user_id, choice_index) in
(select * from (select exercise_question_id, user_id, choice_index from exercise_answers group by exercise_question_id, user_id, choice_index having count(*) > 1) a)
and id not in (select * from (select max(id) from exercise_answers group by exercise_question_id, user_id, choice_index having count(*) > 1 order by id) b))
ActiveRecord::Base.connection.execute sql
add_index :exercise_answers, [:exercise_question_id, :user_id, :choice_index], name: 'exercise_user_choice_index', unique: true
remove_index :exercise_answers, name: :exercise_choice_index
end
end

@ -0,0 +1,13 @@
class Modify1WechatSupportForShixuns < ActiveRecord::Migration[5.2]
def change
shixuns = Shixun.joins(:challenges).where(is_wechat_support: true, status: 2)
.select("shixuns.*, challenges.path path")
shixuns.each do |shixun|
if shixun.path && shixun.path.split("").count > 1
shixun.update_attribute(:is_wechat_support, false)
end
end
Shixun.joins(:challenges).where(challenges: {st: 1}).update_all(is_wechat_support: false)
end
end

@ -9,7 +9,8 @@ namespace :subjects do
buffer_size = 0 buffer_size = 0
column_value = "subject_id, study_count, course_study_count, initiative_study, passed_count, course_used_count, " + column_value = "subject_id, study_count, course_study_count, initiative_study, passed_count, course_used_count, " +
"school_used_count, created_at, updated_at" "school_used_count, created_at, updated_at"
subjects.find_each do |subject| subjects.find_in_batches(batch_size: 50) do |s, index|
Parallel.each_with_index(s, in_processes: 4) do |subject|
puts("---------------------data_statistic: #{subject.id}") puts("---------------------data_statistic: #{subject.id}")
Rails.logger.info("---------------------data_statistic: #{subject.id}") Rails.logger.info("---------------------data_statistic: #{subject.id}")
data = Subjects::DataStatisticService.new(subject) data = Subjects::DataStatisticService.new(subject)
@ -22,7 +23,7 @@ namespace :subjects do
"#{data.passed_count}, #{data.course_used_count}, #{data.school_used_count}, " + "#{data.passed_count}, #{data.course_used_count}, #{data.school_used_count}, " +
"'#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}', '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}')") "'#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}', '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}')")
buffer_size += 1 buffer_size += 1
if buffer_size == 1000 if buffer_size == 1000 || subjects.count == (index+1)
sql = "REPLACE INTO subject_records(#{column_value}) VALUES #{str}" sql = "REPLACE INTO subject_records(#{column_value}) VALUES #{str}"
puts sql puts sql
ActiveRecord::Base.connection.execute sql ActiveRecord::Base.connection.execute sql
@ -30,6 +31,7 @@ namespace :subjects do
buffer_size = 0 buffer_size = 0
end end
end end
end
if buffer_size > 0 if buffer_size > 0
sql = "REPLACE INTO subject_records(#{column_value}) VALUES #{str}" sql = "REPLACE INTO subject_records(#{column_value}) VALUES #{str}"
puts sql puts sql
@ -47,18 +49,20 @@ namespace :subjects do
buffer_size = 0 buffer_size = 0
column_value = "subject_id, school_id, school_name, course_count, student_count, choice_shixun_num, " + column_value = "subject_id, school_id, school_name, course_count, student_count, choice_shixun_num, " +
"choice_shixun_frequency, created_at, updated_at" "choice_shixun_frequency, created_at, updated_at"
subjects.find_each do |subject|
subjects.find_in_batches(batch_size: 50) do |s|
Parallel.each(s, in_processes: 4) do |subject|
puts("---------------------course_info_statistic: #{subject.id}") puts("---------------------course_info_statistic: #{subject.id}")
Rails.logger.info("---------------------course_info_statistic: #{subject.id}") Rails.logger.info("---------------------course_info_statistic: #{subject.id}")
data = Subjects::CourseUsedInfoService.call(subject) data = Subjects::CourseUsedInfoService.call(subject)
Parallel.map(data) do |key| Parallel.map_with_index(data) do |key, index|
next if key[:school_id].nil? next if key[:school_id].nil?
str += ", " unless str.empty? str += ", " unless str.empty?
str += ("(#{subject.id}, #{key[:school_id]}, '#{key[:school_name]}', #{key[:course_count]}, " + str += ("(#{subject.id}, #{key[:school_id]}, '#{key[:school_name]}', #{key[:course_count]}, " +
"#{key[:student_count]}, #{key[:choice_shixun_num]}, #{key[:choice_shixun_frequency]}, " + "#{key[:student_count]}, #{key[:choice_shixun_num]}, #{key[:choice_shixun_frequency]}, " +
"'#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}', '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}')") "'#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}', '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}')")
buffer_size += 1 buffer_size += 1
if buffer_size == 1000 if buffer_size == 1000 || (index + 1) == data.size
sql = "REPLACE INTO subject_course_records(#{column_value}) VALUES #{str}" sql = "REPLACE INTO subject_course_records(#{column_value}) VALUES #{str}"
puts sql puts sql
ActiveRecord::Base.connection.execute sql ActiveRecord::Base.connection.execute sql
@ -67,6 +71,7 @@ namespace :subjects do
end end
end end
end end
end
if buffer_size > 0 if buffer_size > 0
sql = "REPLACE INTO subject_course_records(#{column_value}) VALUES #{str}" sql = "REPLACE INTO subject_course_records(#{column_value}) VALUES #{str}"
puts sql puts sql
@ -84,11 +89,12 @@ namespace :subjects do
buffer_size = 0 buffer_size = 0
column_value = "subject_id, shixun_id, stage, shixun_name, challenge_count, course_count, " + column_value = "subject_id, shixun_id, stage, shixun_name, challenge_count, course_count, " +
"school_count, used_count, passed_count, evaluate_count, passed_ave_time, created_at, updated_at" "school_count, used_count, passed_count, evaluate_count, passed_ave_time, created_at, updated_at"
subjects.find_each(batch_size: 100) do |subject| subjects.find_in_batches(batch_size: 50) do |s|
Parallel.each_with_index(s, in_processes: 4) do |subject|
puts("---------------------shixun_info_statistic: #{subject.id}") puts("---------------------shixun_info_statistic: #{subject.id}")
Rails.logger.info("---------------------shixun_info_statistic: #{subject.id}") Rails.logger.info("---------------------shixun_info_statistic: #{subject.id}")
data = Subjects::ShixunUsedInfoService.call(subject) data = Subjects::ShixunUsedInfoService.call(subject)
data.each do |key| data.each_with_index do |key, index|
next if key[:shixun_id].nil? next if key[:shixun_id].nil?
str += ", " unless str.empty? str += ", " unless str.empty?
str += ("(#{subject.id}, #{key[:shixun_id]}, '#{key[:stage]}', '#{key[:name]}', #{key[:challenge_count]}, " + str += ("(#{subject.id}, #{key[:shixun_id]}, '#{key[:stage]}', '#{key[:name]}', #{key[:challenge_count]}, " +
@ -96,7 +102,7 @@ namespace :subjects do
"#{key[:evaluate_count]}, #{key[:passed_ave_time]}, " + "#{key[:evaluate_count]}, #{key[:passed_ave_time]}, " +
"'#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}', '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}')") "'#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}', '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}')")
buffer_size += 1 buffer_size += 1
if buffer_size == 1000 if buffer_size == 1000 || (index+1) == data.size
sql = "REPLACE INTO subject_shixun_infos(#{column_value}) VALUES #{str}" sql = "REPLACE INTO subject_shixun_infos(#{column_value}) VALUES #{str}"
puts sql puts sql
ActiveRecord::Base.connection.execute sql ActiveRecord::Base.connection.execute sql
@ -105,6 +111,7 @@ namespace :subjects do
end end
end end
end end
end
if buffer_size > 0 if buffer_size > 0
sql = "REPLACE INTO subject_shixun_infos(#{column_value}) VALUES #{str}" sql = "REPLACE INTO subject_shixun_infos(#{column_value}) VALUES #{str}"
puts sql puts sql
@ -124,7 +131,7 @@ namespace :subjects do
"code_line_count, evaluate_count, cost_time, created_at, updated_at" "code_line_count, evaluate_count, cost_time, created_at, updated_at"
subjects.find_in_batches(batch_size: 50) do |s| subjects.find_in_batches(batch_size: 50) do |s|
Parallel.each(s, in_processes: 5) do |subject| Parallel.each_with_index(s, in_processes: 4) do |subject, index|
puts("---------------------user_info_statistic: #{subject.id}") puts("---------------------user_info_statistic: #{subject.id}")
data = Subjects::UserUsedInfoService.call(subject) data = Subjects::UserUsedInfoService.call(subject)
data.each do |key| data.each do |key|
@ -134,7 +141,7 @@ namespace :subjects do
"#{key[:passed_games_count]}, #{key[:code_line_count]}, #{key[:evaluate_count]}, #{key[:cost_time]}, " + "#{key[:passed_games_count]}, #{key[:code_line_count]}, #{key[:evaluate_count]}, #{key[:cost_time]}, " +
"'#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}', '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}')") "'#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}', '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}')")
buffer_size += 1 buffer_size += 1
if buffer_size == 1000 if buffer_size == 1000 || (index+1 == data.size)
sql = "REPLACE INTO subject_user_infos(#{column_value}) VALUES #{str}" sql = "REPLACE INTO subject_user_infos(#{column_value}) VALUES #{str}"
ActiveRecord::Base.connection.execute sql ActiveRecord::Base.connection.execute sql
str = "" str = ""

@ -1,33 +1,70 @@
# 执行示例 bundle exec rake zip_pack:shixun_pack args=123,2323 # 执行示例 bundle exec rake zip_pack:shixun_pack class=Course ids=123,2323 parallel_size=4
# 执行示例 bundle exec rake zip_pack:shixun_pack class=HomeworkCommon ids=123,2323
namespace :zip_pack do namespace :zip_pack do
desc "手工打包作品" desc "手工打包作品"
OUTPUT_FOLDER = "#{Rails.root}/files/archiveZip" OUTPUT_FOLDER = "#{Rails.root}/files/archiveZip"
task :shixun_pack => :environment do task :shixun_pack => :environment do
if ENV['args'] if ENV['class'] && ENV['ids']
homework_ids = ENV['args'].split(",").map(&:to_i) parallel_size = ENV['parallel_size'] || 2
homeworks = HomeworkCommon.where(id: homework_ids) parallel_size = parallel_size.to_i
env_ids = ENV['ids'].split(",").map(&:to_i)
folders = []
if ENV['class'] == "Course"
courses = Course.where(id: env_ids)
courses.each do |course|
homeworks = course.practice_homeworks.homework_published
new_dir_name = "#{course.name.to_s.strip}_#{Time.now.strftime("%Y%m%d%H%M%S").to_s}"
new_dir_name.gsub!(" ", "-")
new_dir_name.gsub!("/", "_")
new_folder = "#{OUTPUT_FOLDER}/#{new_dir_name}"
zip_homework_pdf homeworks, new_folder, parallel_size
folders << new_folder
end
else
homeworks = HomeworkCommon.where(id: env_ids)
new_dir_name = "#{homeworks.first&.course&.name.to_s.strip}_#{Time.now.strftime("%Y%m%d%H%M%S").to_s}"
new_dir_name.gsub!(" ", "-")
new_dir_name.gsub!("/", "_")
new_folder = "#{OUTPUT_FOLDER}/#{new_dir_name}"
zip_homework_pdf homeworks, new_folder, parallel_size
folders << new_folder
end
puts "下载路径: #{folders.join(",")}"
end
end
def zip_homework_pdf homeworks, folder, parallel_size
Dir.mkdir(folder) unless File.directory?(folder)
homeworks.includes(:score_student_works).each do |homework| homeworks.includes(:score_student_works).each do |homework|
out_file_name = "#{Time.now.strftime("%Y%m%d%H%M%S").to_s}-#{homework.course_id}-#{homework.name}.zip" out_file_name = "#{Time.now.strftime("%Y%m%d%H%M%S").to_s}-#{homework.course_id}-#{homework.name}.zip"
out_file_name.gsub!(" ", "-") out_file_name.gsub!(" ", "-")
out_file_name.gsub!("/", "_") out_file_name.gsub!("/", "_")
zipfile_name = "#{OUTPUT_FOLDER}/#{out_file_name}" zipfile_name = "#{folder}/#{out_file_name}"
Dir.mkdir(File.dirname(zipfile_name)) unless File.exist?(File.dirname(zipfile_name))
student_works = homework.score_student_works student_works = homework.score_student_works
if student_works.size > 0 if student_works.size > 0
pdfs = [] pdfs = []
Zip::File.open(zipfile_name, Zip::File::CREATE) do |zip| file_paths = []
student_works.find_each.map do |student_work| student_works.find_in_batches(batch_size: 500) do |sw|
Parallel.each(sw, in_threads: parallel_size) do |student_work|
export = ExportShixunReportService.new(homework, student_work) export = ExportShixunReportService.new(homework, student_work)
pdf = export.to_pdf pdf = export.to_pdf
pdfs << pdf pdfs << pdf
begin file_paths << {filename: export.filename, path: pdf.path}
zip.add(export.filename, pdf.path)
puts "out: #{export.filename}_#{pdf.path}" puts "out: #{export.filename}_#{pdf.path}"
end
end
Zip::File.open(zipfile_name, Zip::File::CREATE) do |zip|
file_paths.each do |pdf|
begin
zip.add(pdf[:filename], pdf[:path])
rescue => ex rescue => ex
Rails.logger.error(ex.message) Rails.logger.error(ex.message)
@ -36,16 +73,13 @@ namespace :zip_pack do
end end
end end
end end
zipfile = zipfile_name
else else
zipfile = {:message => "no file"} zipfile = {:message => "no file"}
end end
puts "out: #{zipfile}"
end
end end
end end
# 执行示例 bundle exec rake zip_pack:homework_attach_pack args=123
task :homework_attach_pack => :environment do task :homework_attach_pack => :environment do
include ExportHelper include ExportHelper
if ENV['args'] if ENV['args']
@ -61,7 +95,4 @@ namespace :zip_pack do
end end
end end
def filename_for_content_disposition(name)
request.env['HTTP_USER_AGENT'] =~ %r{MSIE|Trident|Edge} ? ERB::Util.url_encode(name) : name
end
end end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 128 KiB

@ -75,6 +75,7 @@
"react-codemirror": "^1.0.0", "react-codemirror": "^1.0.0",
"react-codemirror2": "^6.0.0", "react-codemirror2": "^6.0.0",
"react-content-loader": "^3.1.1", "react-content-loader": "^3.1.1",
"react-cookies": "^0.1.1",
"react-dev-utils": "^5.0.0", "react-dev-utils": "^5.0.0",
"react-dom": "^16.9.0", "react-dom": "^16.9.0",
"react-hot-loader": "^4.0.0", "react-hot-loader": "^4.0.0",

@ -1134,6 +1134,12 @@
<div class="code-name">&amp;#xe7f9;</div> <div class="code-name">&amp;#xe7f9;</div>
</li> </li>
<li class="dib">
<span class="icon iconfont">&#xe71b;</span>
<div class="name">过滤器</div>
<div class="code-name">&amp;#xe71b;</div>
</li>
<li class="dib"> <li class="dib">
<span class="icon iconfont">&#xe6ee;</span> <span class="icon iconfont">&#xe6ee;</span>
<div class="name">20从属连接</div> <div class="name">20从属连接</div>
@ -1866,6 +1872,18 @@
<div class="code-name">&amp;#xe719;</div> <div class="code-name">&amp;#xe719;</div>
</li> </li>
<li class="dib">
<span class="icon iconfont">&#xe71c;</span>
<div class="name">初始化</div>
<div class="code-name">&amp;#xe71c;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe71e;</span>
<div class="name">测试集</div>
<div class="code-name">&amp;#xe71e;</div>
</li>
</ul> </ul>
<div class="article markdown"> <div class="article markdown">
<h2 id="unicode-">Unicode 引用</h2> <h2 id="unicode-">Unicode 引用</h2>
@ -3572,6 +3590,15 @@
</div> </div>
</li> </li>
<li class="dib">
<span class="icon iconfont icon-guolvqi"></span>
<div class="name">
过滤器
</div>
<div class="code-name">.icon-guolvqi
</div>
</li>
<li class="dib"> <li class="dib">
<span class="icon iconfont icon-congshulianjie"></span> <span class="icon iconfont icon-congshulianjie"></span>
<div class="name"> <div class="name">
@ -4670,6 +4697,24 @@
</div> </div>
</li> </li>
<li class="dib">
<span class="icon iconfont icon-chushihua"></span>
<div class="name">
初始化
</div>
<div class="code-name">.icon-chushihua
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-ceshiji"></span>
<div class="name">
测试集
</div>
<div class="code-name">.icon-ceshiji
</div>
</li>
</ul> </ul>
<div class="article markdown"> <div class="article markdown">
<h2 id="font-class-">font-class 引用</h2> <h2 id="font-class-">font-class 引用</h2>
@ -6171,6 +6216,14 @@
<div class="code-name">#icon-gengduo1</div> <div class="code-name">#icon-gengduo1</div>
</li> </li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-guolvqi"></use>
</svg>
<div class="name">过滤器</div>
<div class="code-name">#icon-guolvqi</div>
</li>
<li class="dib"> <li class="dib">
<svg class="icon svg-icon" aria-hidden="true"> <svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-congshulianjie"></use> <use xlink:href="#icon-congshulianjie"></use>
@ -7147,6 +7200,22 @@
<div class="code-name">#icon-jiashang1</div> <div class="code-name">#icon-jiashang1</div>
</li> </li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-chushihua"></use>
</svg>
<div class="name">初始化</div>
<div class="code-name">#icon-chushihua</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-ceshiji"></use>
</svg>
<div class="name">测试集</div>
<div class="code-name">#icon-ceshiji</div>
</li>
</ul> </ul>
<div class="article markdown"> <div class="article markdown">
<h2 id="symbol-">Symbol 引用</h2> <h2 id="symbol-">Symbol 引用</h2>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1293,6 +1293,13 @@
"unicode": "e7f9", "unicode": "e7f9",
"unicode_decimal": 59385 "unicode_decimal": 59385
}, },
{
"icon_id": "5327531",
"name": "过滤器",
"font_class": "guolvqi",
"unicode": "e71b",
"unicode_decimal": 59163
},
{ {
"icon_id": "5379378", "icon_id": "5379378",
"name": "20从属连接", "name": "20从属连接",
@ -2146,6 +2153,20 @@
"font_class": "jiashang1", "font_class": "jiashang1",
"unicode": "e719", "unicode": "e719",
"unicode_decimal": 59161 "unicode_decimal": 59161
},
{
"icon_id": "12826208",
"name": "初始化",
"font_class": "chushihua",
"unicode": "e71c",
"unicode_decimal": 59164
},
{
"icon_id": "12826211",
"name": "测试集",
"font_class": "ceshiji",
"unicode": "e71e",
"unicode_decimal": 59166
} }
] ]
} }

@ -572,6 +572,9 @@ Created by iconfont
<glyph glyph-name="gengduo1" unicode="&#59385;" d="M104 376m-104 0a104 104 0 1 1 208 0 104 104 0 1 1-208 0ZM920 376m-104 0a104 104 0 1 1 208 0 104 104 0 1 1-208 0ZM512 384m-160 0a160 160 0 1 1 320 0 160 160 0 1 1-320 0Z" horiz-adv-x="1024" /> <glyph glyph-name="gengduo1" unicode="&#59385;" d="M104 376m-104 0a104 104 0 1 1 208 0 104 104 0 1 1-208 0ZM920 376m-104 0a104 104 0 1 1 208 0 104 104 0 1 1-208 0ZM512 384m-160 0a160 160 0 1 1 320 0 160 160 0 1 1-320 0Z" horiz-adv-x="1024" />
<glyph glyph-name="guolvqi" unicode="&#59163;" d="M908.6 761.3c6.5-15.5 3.8-28.8-8-39.7L620.9 441.9V21c0-15.8-7.4-27.1-22.1-33.5-4.9-2-9.7-2.9-14.2-2.9-10.3 0-18.7 3.6-25.5 10.7L413.8 140.8c-7.2 7.2-10.7 15.7-10.7 25.5V441.9L123.4 721.6c-11.7 11-14.3 24.2-8 39.7 6.5 14.8 17.5 22.1 33.5 22.1H875c16 0.1 27.2-7.3 33.6-22.1z" horiz-adv-x="1024" />
<glyph glyph-name="congshulianjie" unicode="&#59118;" d="M844.8 230.4a128 128 0 0 1-125.44-102.4H358.4a102.4 102.4 0 0 0 0 204.8h307.2a153.6 153.6 0 0 1 0 307.2H304.64a128 128 0 1 1 0-51.2H665.6a102.4 102.4 0 0 0 0-204.8H358.4a153.6 153.6 0 0 1 0-307.2h360.96a128 128 0 1 1 125.44 153.6z m0-204.8a76.8 76.8 0 1 0 76.8 76.8 76.8 76.8 0 0 0-76.8-76.8z" horiz-adv-x="1024" /> <glyph glyph-name="congshulianjie" unicode="&#59118;" d="M844.8 230.4a128 128 0 0 1-125.44-102.4H358.4a102.4 102.4 0 0 0 0 204.8h307.2a153.6 153.6 0 0 1 0 307.2H304.64a128 128 0 1 1 0-51.2H665.6a102.4 102.4 0 0 0 0-204.8H358.4a153.6 153.6 0 0 1 0-307.2h360.96a128 128 0 1 1 125.44 153.6z m0-204.8a76.8 76.8 0 1 0 76.8 76.8 76.8 76.8 0 0 0-76.8-76.8z" horiz-adv-x="1024" />
@ -938,6 +941,12 @@ Created by iconfont
<glyph glyph-name="jiashang1" unicode="&#59161;" d="M512-128C228.693333-128 0 100.693333 0 384S228.693333 896 512 896s512-228.693333 512-512-228.693333-512-512-512z m0 989.866667C249.173333 861.866667 34.133333 646.826667 34.133333 384s215.04-477.866667 477.866667-477.866667 477.866667 215.04 477.866667 477.866667S774.826667 861.866667 512 861.866667zM699.733333 332.8h-375.466666c-20.48 0-34.133333 13.653333-34.133334 34.133333s13.653333 34.133333 34.133334 34.133334h375.466666c20.48 0 34.133333-13.653333 34.133334-34.133334s-13.653333-34.133333-34.133334-34.133333zM512 145.066667c-20.48 0-34.133333 13.653333-34.133333 34.133333V554.666667c0 20.48 13.653333 34.133333 34.133333 34.133333s34.133333-13.653333 34.133333-34.133333v-375.466667c0-20.48-13.653333-34.133333-34.133333-34.133333z" horiz-adv-x="1024" /> <glyph glyph-name="jiashang1" unicode="&#59161;" d="M512-128C228.693333-128 0 100.693333 0 384S228.693333 896 512 896s512-228.693333 512-512-228.693333-512-512-512z m0 989.866667C249.173333 861.866667 34.133333 646.826667 34.133333 384s215.04-477.866667 477.866667-477.866667 477.866667 215.04 477.866667 477.866667S774.826667 861.866667 512 861.866667zM699.733333 332.8h-375.466666c-20.48 0-34.133333 13.653333-34.133334 34.133333s13.653333 34.133333 34.133334 34.133334h375.466666c20.48 0 34.133333-13.653333 34.133334-34.133334s-13.653333-34.133333-34.133334-34.133333zM512 145.066667c-20.48 0-34.133333 13.653333-34.133333 34.133333V554.666667c0 20.48 13.653333 34.133333 34.133333 34.133333s34.133333-13.653333 34.133333-34.133333v-375.466667c0-20.48-13.653333-34.133333-34.133333-34.133333z" horiz-adv-x="1024" />
<glyph glyph-name="chushihua" unicode="&#59164;" d="M511.682434-128A468.457376 468.457376 0 0 0 44.559694 340.457376a66.73182 66.73182 0 0 0 133.46364 0A333.6591 333.6591 0 1 1 511.682434 675.451113a330.989827 330.989827 0 0 1-235.785764-98.095776 66.73182 66.73182 0 0 0-111.2197 66.73182l60.503517 203.309612a66.798552 66.798552 0 1 0 128.125094-37.814698l-9.564894-31.808835A468.234937 468.234937 0 1 0 511.682434-128zM600.658194 183.41516h-133.46364a66.73182 66.73182 0 0 0-66.73182 66.73182V472.58638a66.73182 66.73182 0 0 0 133.46364 0v-155.70758h66.73182a66.73182 66.73182 0 0 0 0-133.46364z" horiz-adv-x="1024" />
<glyph glyph-name="ceshiji" unicode="&#59166;" d="M536.332172 333.97898a75.330291 75.330291 0 0 0-29.985844 6.338471l-463.195967 207.95061a73.136205 73.136205 0 0 0 0 133.351681l463.195967 207.95061a73.136205 73.136205 0 0 0 59.971688 0l463.195966-207.95061a73.136205 73.136205 0 0 0 0-133.351681l-463.195966-207.95061a75.330291 75.330291 0 0 0-29.985844-6.338471zM251.832333 614.822008L536.332172 487.321224l284.499838 127.500784L536.332172 742.56658zM536.332172 102.868571a73.136205 73.136205 0 0 0-31.204781 7.069834l-463.195967 219.408615a73.184963 73.184963 0 0 0 62.409562 132.376532L536.332172 257.429752l431.259823 215.020443a73.136205 73.136205 0 0 0 65.33501-130.913807l-463.195966-230.866621a73.136205 73.136205 0 0 0-33.398867-7.801196zM536.332172-127.99805a73.136205 73.136205 0 0 0-31.204781 7.069833l-463.195967 219.408616a73.136205 73.136205 0 0 0 62.409562 131.888957L536.332172 26.319343l431.259823 215.264231a73.136205 73.136205 0 0 0 65.33501-130.913807l-463.195966-230.866621A73.136205 73.136205 0 0 0 536.332172-127.99805z" horiz-adv-x="1073" />
</font> </font>

Before

Width:  |  Height:  |  Size: 381 KiB

After

Width:  |  Height:  |  Size: 383 KiB

@ -17,6 +17,7 @@
<meta name=”Description” Content=”EduCoder翻转课堂教学模式颠覆了传统教学模式让教师与学生的关系由“权威”变成了“伙伴”。将学习的主动权转交给学生使学生可个性化化学学生的学习主体得到了彰显。”> <meta name=”Description” Content=”EduCoder翻转课堂教学模式颠覆了传统教学模式让教师与学生的关系由“权威”变成了“伙伴”。将学习的主动权转交给学生使学生可个性化化学学生的学习主体得到了彰显。”>
<meta name=”Description” Content=”EduCoder实训项目为单个知识点关卡实践训练帮助学生巩固单一弱点强化学习。 > <meta name=”Description” Content=”EduCoder实训项目为单个知识点关卡实践训练帮助学生巩固单一弱点强化学习。 >
<meta name=”Description” Content=”EduCoder实践教学平台各类大赛为进一步提高各类学生综合运用高级语言程序设计能力培养创新意识和实践探索精神发掘优秀软件人才。 > <meta name=”Description” Content=”EduCoder实践教学平台各类大赛为进一步提高各类学生综合运用高级语言程序设计能力培养创新意识和实践探索精神发掘优秀软件人才。 >
<meta name="viewport" id="viewport" content="width=device-width, initial-scale=0.3, maximum-scale=0.3">
<meta name="theme-color" content="#000000"> <meta name="theme-color" content="#000000">

@ -366,6 +366,11 @@ const JupyterTPI = Loadable({
loader: () => import('./modules/tpm/jupyter'), loader: () => import('./modules/tpm/jupyter'),
loading: Loading loading: Loading
}); });
// 微信代码编辑器
const WXCode = Loadable({
loader: () => import('./modules/wxcode'),
loading: Loading
});
// //个人竞赛报名 // //个人竞赛报名
// const PersonalCompetit = Loadable({ // const PersonalCompetit = Loadable({
// loader: () => import('./modules/competition/personal/PersonalCompetit.js'), // loader: () => import('./modules/competition/personal/PersonalCompetit.js'),
@ -823,6 +828,11 @@ class App extends Component {
render={ render={
(props) => (<Headplugselection {...this.props} {...props} {...this.state} />) (props) => (<Headplugselection {...this.props} {...props} {...this.state} />)
}/> }/>
<Route path="/wxcode/:identifier?" component={WXCode}
render={
(props)=>(<WXCode {...this.props} {...props} {...this.state}></WXCode>)
}
/>
<Route exact path="/" <Route exact path="/"
// component={ShixunsHome} // component={ShixunsHome}
render={ render={

@ -5,6 +5,7 @@ import md5 from 'md5';
import { requestProxy } from "./indexEduplus2RequestProxy"; import { requestProxy } from "./indexEduplus2RequestProxy";
import { broadcastChannelOnmessage ,SetAppModel, isDev, queryString } from 'educoder'; import { broadcastChannelOnmessage ,SetAppModel, isDev, queryString } from 'educoder';
import { notification } from 'antd'; import { notification } from 'antd';
import cookie from 'react-cookies';
import './index.css'; import './index.css';
const $ = window.$; const $ = window.$;
const opens ="79e33abd4b6588941ab7622aed1e67e8"; const opens ="79e33abd4b6588941ab7622aed1e67e8";
@ -24,6 +25,18 @@ function locationurl(list){
} }
function setCookier(){
const _params = window.location.search;
if (_params) {
let _search = _params.split('?')[1];
_search.split('&').forEach(item => {
console.log(item);
const _arr = item.split('=');
cookie.save('_educoder_session',_arr[0]);
cookie.save('autologin_trustie',_arr[1]);
});
}
}
// TODO 开发期多个身份切换 // TODO 开发期多个身份切换
let debugType ="" let debugType =""
@ -87,6 +100,31 @@ export function initAxiosInterceptors(props) {
// proxy="https://test-newweb.educoder.net" // proxy="https://test-newweb.educoder.net"
// proxy="https://test-jupyterweb.educoder.net" // proxy="https://test-jupyterweb.educoder.net"
//proxy="http://192.168.2.63:3001" //proxy="http://192.168.2.63:3001"
try {
const str =window.location.pathname;
if(str.indexOf("/wxcode") !== -1){
// console.log("开始重写cookis");
const _params = window.location.search;
// console.log("1111");
if (_params) {
// console.log("22222");
let _search = _params.split('?')[1];
_search.split('&').forEach(item => {
const _arr = item.split('=');
if(_arr[0]==='_educoder_session'){
cookie.save('_educoder_session',_arr[1],{ path: '/' });
cookie.save('_educoder_session',_arr[1], { domain: '.educoder.net', path: '/'});
}else{
cookie.save('autologin_trustie',_arr[1],{ path: '/' });
cookie.save('autologin_trustie',_arr[1], { domain: '.educoder.net', path: '/'});
}
});
}
}
}catch (e) {
}
// 在这里使用requestMap控制避免用户通过双击等操作发出重复的请求 // 在这里使用requestMap控制避免用户通过双击等操作发出重复的请求
// 如果需要支持重复的请求考虑config里面自定义一个allowRepeat参考来控制 // 如果需要支持重复的请求考虑config里面自定义一个allowRepeat参考来控制
@ -96,7 +134,7 @@ export function initAxiosInterceptors(props) {
requestMap[keyName] = false; requestMap[keyName] = false;
} }
//响应前的设置
axios.interceptors.request.use( axios.interceptors.request.use(
config => { config => {
// config.headers['Content-Type']= 'no-cache' // config.headers['Content-Type']= 'no-cache'
@ -111,6 +149,56 @@ export function initAxiosInterceptors(props) {
// proxy = 'http://localhost:3000' // proxy = 'http://localhost:3000'
// } // }
// --------------------------------------------- // ---------------------------------------------
// console.log("开始请求了");
// console.log(config.url);
// console.log(window.location.pathname);
//
// try {
// const str =window.location.pathname;
// if(str.indexOf("/wxcode") !== -1){
// // console.log("开始重写cookis");
// const _params = window.location.search;
// // console.log("1111");
// if (_params) {
// // console.log("22222");
// let _search = _params.split('?')[1];
// var _educoder_sessionmys="";
// var autologin_trusties="";
// _search.split('&').forEach(item => {
// const _arr = item.split('=');
// if(_arr[0]==='_educoder_session'){
// cookie.save('_educoder_session',_arr[1], { domain: '.educoder.net', path: '/'});
// _educoder_sessionmys=_arr[1];
// }else{
// cookie.save('autologin_trustie',_arr[1], { domain: '.educoder.net', path: '/'});
// autologin_trusties=_arr[1];
// }
// });
// try {
// const autlogins= `_educoder_session=${_educoder_sessionmys}; autologin_trustie=${autologin_trusties} `;
// config.params = {'Cookie': autlogins}
// config.headers['Cookie'] =autlogins;
// // console.log("设置了cookis");
// } catch (e) {
//
// }
// try {
// const autloginysls= `_educoder_session=${_educoder_sessionmys}; autologin_trustie=${autologin_trusties} `;
// config.params = {'autloginysls': autloginysls}
// config.headers['Cookie'] =autloginysls;
// // console.log("设置了cookis");
// }catch (e) {
//
// }
// }
// }
// }catch (e) {
//
// }
if (config.url.indexOf(proxy) != -1 || config.url.indexOf(':') != -1) { if (config.url.indexOf(proxy) != -1 || config.url.indexOf(':') != -1) {
return config return config
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

@ -0,0 +1,381 @@
/*
* @Description: 微信端代码编辑器
* @Author: tangjiang
* @Github:
* @Date: 2020-01-15 09:56:34
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-18 15:07:09
*/
import './index.scss';
import React, {useState, useEffect, useRef} from 'react';
import MonacoEditor from '@monaco-editor/react';
import { Input, Icon } from 'antd';
import { connect } from 'react-redux';
import actions from '../../redux/actions';
import cookie from 'react-cookies';
const { TextArea } = Input;
const App = (props) => {
const {
isShow,
wxCode,
path,
showLoading,
showDialog,
gold,
experience,
next_game,
// userCode,
testCase = [],
getWXCode,
last_compile_output,
test_sets_count,
sets_error_count,
getWXCodeTestCase,
restoreWXCode,
updateWXCodeForEditor,
updateWXCodeForInterval,
evaluateWxCode,
showWXCodeTextCase,
changeWXCodeEvaluateLoading,
changeWXCodeEvaluateDialog
} = props;
const {identifier} = props.match.params;
// 获取路径参数
const setCookier = () => {
const _params = window.location.search;
if (_params) {
let _search = _params.split('?')[1];
_search.split('&').forEach(item => {
// console.log(item);
const _arr = item.split('=');
cookie.remove(_arr[0], {
path: '/',
domain: '.educoder.net'
});
cookie.save(_arr[0], _arr[1], {
path: '/',
domain: '.educoder.net'
});
});
}
}
setCookier();
const [isActive, setIsActive] = useState(-1);
const [tip, setTip] = useState('');
const [codes, setCodes] = useState(wxCode);
// const [showInfo, setShowInfo] = useState(false);
// const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
if (codes !== props.wxCode) {
setCodes(props.wxCode);
}
}, [props]);
const editorRef = useRef(null);
let timer = null;
const loadResult = (identifier) => {
// 加载代码块内容
getWXCode(identifier);
// 加载测试集
const params = {
path,
status: 0,
retry: 1
};
getWXCodeTestCase(identifier, params);
}
useEffect(() => {
setTimeout(() => {
setCookier();
loadResult(identifier);
}, 0);
}, []);
// 关闭
const handleCloseTestCase = () => {
// setIsVisible(false);
showWXCodeTextCase(false)
}
// 测试集
const handleClickTestCase = () => {
// setIsVisible(true);
showWXCodeTextCase(true)
}
// 编辑器代码
const handleEditorChange = (origin, monaco) => {
editorRef.current = monaco; // 获取当前monaco实例
// setEditCode(origin); // 保存编辑器初始值
editorRef.current.onDidChangeModelContent(e => { // 监听编辑器内容的变化
// TODO 需要优化 节流
const val = editorRef.current.getValue();
// console.log('编辑器代码====>>>>', val);
// updateWXCodeForEditor(val);
codeChange(val);
});
};
const codeChange = (code) => {
// console.log(code);
updateWXCodeForEditor(code);
if (!timer) {
timer = setInterval(function () {
clearInterval(timer);
timer = null;
// 调用更新代码
updateWXCodeForInterval(identifier, path);
}, 5000);
}
}
// 关闭单个测试集
const handleCloseItem = (i, flag) => {
if (!flag) return;
setIsActive(isActive === i ? -1 : i);
}
// 初始化
const handleResetCode = () => {
clearInterval(timer);
timer = null;
const result = window.confirm('你在本文件中修改的内容将丢失, 是否确定重新加载初始代码?');
if (result) {
setTip('初始化中...');
changeWXCodeEvaluateLoading(true);
identifier && restoreWXCode(identifier, { path });
}
}
// 评测
const handleEvalateCode = () => {
changeWXCodeEvaluateLoading(true);
evaluateWxCode(identifier, path);
setTip('评测中...');
}
// 关闭弹框
const handleCloseDialog = () => {
changeWXCodeEvaluateDialog(false);
}
// 查看测试集
const handleClickShowResult = () => {
showWXCodeTextCase(true);
changeWXCodeEvaluateDialog(false);
}
// 下一关
const handleClickNext = () => {
changeWXCodeEvaluateDialog(false);
loadResult(next_game);
}
const tcclasses = isShow ? `wx-code-test-case active` : 'wx-code-test-case';
const loading = showLoading ? 'code-evaluate-loading active' : 'code-evaluate-loading';
const dialog = showDialog ? 'pass-dialog active' : 'pass-dialog';
const _val = +sets_error_count === 0;
let resultTxt = (_val) ? '全部通过' : `${sets_error_count}组测试结果不匹配`;
const iclasses = _val ? 'iconfont icon-wancheng icon success' : 'iconfont icon-tishi1 icon fail';
const tclasses = _val ? 'result-txt success' : 'result-txt fail';
const ulClasses = !!last_compile_output ? 'case-list hasResult' : 'case-list';
const resultFlag = !!last_compile_output ? 'wxcode-test-result active' : 'wxcode-test-result';
// console.log('==========>>>>>>>> 评测结果样式', last_compile_output, resultFlag);
return (
<div className="wx-code-area">
<div className="wx-code-flex">
<div className="wx-code-item">
<MonacoEditor
height="100%"
width="100%"
language="python"
value={codes}
options={{
selectOnLineNumbers: true,
automaticLayout: true,
fontSize: `42px`
}}
theme='dark'
editorDidMount={handleEditorChange}
/>
</div>
<div className="wx-code-test">
<div className="flex-btn">
<span className="icon-btn" onClick={handleResetCode}>
<i className="iconfont icon-chushihua icon"></i>
<span className="icon-txt">初始化</span>
</span>
<span className="icon-btn" onClick={handleClickTestCase}>
<i className="iconfont icon-ceshiji icon"></i>
<span className="icon-txt">测试集</span>
</span>
</div>
{/* <Button type="primary" shape="circle">评测</Button> */}
<button className="wx-pt-btn" onClick={handleEvalateCode}>评测</button>
</div>
</div>
{/* 测试集 */}
<div className={tcclasses}>
<div className="text-case-list">
<div className="list-header">
<span className="header-title">{testCase.length}个测试用例</span>
<span className="header-close" onClick={handleCloseTestCase}>关闭</span>
</div>
<div className={resultFlag}>
<i className={iclasses}></i>
<span className={tclasses}>{test_sets_count - sets_error_count}/{test_sets_count}</span>
<span className={`${tclasses} result-txt-desc`}>{resultTxt}</span>
</div>
<ul className={ulClasses}>
{
testCase.map((item, i) => {
const {input, output, actual_output, is_public, result, compile_success} = item;
const _classes = isActive === i ? 'case-item-desc active' : 'case-item-desc';
const iconclasses = isActive === i ? 'iconfont icon-sanjiaoxing-down icon active' : 'iconfont icon-triangle icon';
const headerClasses = is_public ? 'item-header-desc active' : 'item-header-desc';
// console.log(_classes);
return (
<li className="case-item" key={`item_${i}`}>
<div className="case-item-header" onClick={() => handleCloseItem(i, is_public)}>
<h2 className={headerClasses}>
<i className={iconclasses}></i>
测试集{i + 1}
</h2>
{
is_public
? compile_success > 0
? result ? <span className="iconfont icon-wancheng case_item_success"></span>
: <span className="iconfont icon-jinggao1 case_item_fail"></span>
: ''
:
<span className="case-item-tips">隐藏测试集暂不支持解锁和查看 </span>
// is_public
// ? result ? <span className="iconfont icon-wancheng case_item_success"></span>
// : <span className="iconfont icon-jinggao1 case_item_fail"></span>)
// : (
// 隐藏测试集,暂不支持解锁和查看
// {/* {result
// ? <span className="iconfont icon-wancheng case_item_success"></span>
// : <span className="iconfont icon-jinggao1 case_item_fail"></span>
// } */}
// </span>)
}
</div>
<div className={_classes}>
<span className="desc-title">测试输入</span>
<span className="test-input">{input || '-'}</span>
<span className="desc-title">预期输出</span>
{/* <textarea rows="5">预期输出</textarea> */}
<TextArea
readOnly={true}
className="text-area-style"
value={output}
onChange={this.onChange}
placeholder="Controlled autosize"
autoSize={{ minRows: 3, maxRows: 6 }}
/>
{/* <TextArea rows={5} className="hope-result">预期输出</TextArea> */}
<span className="desc-title">实际输出</span>
<TextArea
readOnly={true}
className="text-area-style"
value={actual_output}
autoSize={{ minRows: 1, maxRows: 3 }}
/>
</div>
</li>
)
})
}
</ul>
</div>
</div>
{/* 测评中 */}
<div className={loading}>
<span className="loading-flex">
<Icon className="loading-icon" type="loading" />
<span className="loading-txt">{tip}</span>
</span>
</div>
{/* 通过弹框 */}
<div className={dialog}>
<div className="pass-box">
<div className="pass-img"></div>
<div className="pass-ctx">
<div className="pass-title">{next_game ? '评测通过' : '恭喜通关'}</div>
<p className="pass-value">
金币 <span className="value_color">{gold > 0 ? `+${gold}` : 0}</span>,
经验值 <span className="value_color">{experience > 0 ? `+${experience}` : 0}</span>
</p>
{
next_game
? (
<div className="pass-btn">
<button className="btn btn-first" onClick={handleClickShowResult}>评测结果</button>
<button className="btn btn-second" onClick={handleClickNext}>下一关</button>
</div>
)
: (
<div className="pass-btn-all">
<button className="btn btn-second" onClick={handleClickShowResult}>评测结果</button>
</div>
)
}
</div>
</div>
<i className="iconfont icon-roundclose icon_close" onClick={handleCloseDialog}></i>
</div>
</div>
);
}
const mapStateToProps = (state) => {
const {
path,
isShow,
wxCode,
userCode,
gold,
experience,
next_game,
testCase,
showLoading,
showDialog,
last_compile_output,
test_sets_count,
sets_error_count
} = state.wxcodeReducer;
// console.log(state);
return {
path,
isShow,
wxCode,
userCode,
gold,
experience,
next_game,
testCase,
showLoading,
showDialog,
last_compile_output,
test_sets_count,
sets_error_count
};
}
const mapDispatchToProps = (dispatch) => ({
getWXCode: (identifier) => dispatch(actions.getWXCode(identifier)),
getWXCodeTestCase: (identifier, params) => dispatch(actions.getWXCodeTestCase(identifier, params)),
restoreWXCode: (identifier, params) => dispatch(actions.restoreWXCode(identifier, params)),
updateWXCodeForEditor: (code) => dispatch(actions.updateWXCodeForEditor(code)),
updateWXCodeForInterval: (identifier, path) => dispatch(actions.updateWXCodeForInterval(identifier, path)),
evaluateWxCode: (identifier, path) => dispatch(actions.evaluateWxCode(identifier, path)),
showWXCodeTextCase: (flag) => dispatch(actions.showWXCodeTextCase(flag)),
changeWXCodeEvaluateLoading: (flag) => dispatch(actions.changeWXCodeEvaluateLoading(flag)),
changeWXCodeEvaluateDialog: (flag) => dispatch(actions.changeWXCodeEvaluateDialog(flag))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);

@ -0,0 +1,367 @@
.wx-code-area{
height: 100vh;
overflow: hidden;
.wx-code-flex{
display: flex;
position: relative;
flex-direction: column;
height: 100%;
}
.wx-code-item{
flex: 1;
}
.wx-code-test{
display: flex;
justify-content: space-between;
align-items: center;
height: 150px;
background-color: #052645;
padding: 0 50px;
}
.flex-btn{
display: flex;
.icon-btn{
display: flex;
flex-direction: column;
color: #2EA4FF;
align-items: center;
.icon{
// font-size: 24px !important;
// transform: scale(2.4);
// line-height: 2;
font-size: 34px !important;
transform: scale(1.8);
position: relative;
// top: 10px;
}
// .icon-reset{
// transform: scale(2.4);
// }
// .icon:first-child{
// transform: scale((3));
// }
&:last-child{
margin-left: 60px;
}
.icon-txt{
// margin-top: 5px;
font-size: 32px;
}
}
}
.wx-pt-btn{
border: none;
outline: none;
border-radius: 9999px;
padding: 0 50px;
line-height: 92px;
font-size: 38px;
color: #fff;
background:#2EA4FF;
letter-spacing: 2;
}
.wx-code-test-case{
position: fixed;
bottom: 0;
left: 0;
right: 0;
top: 0;
transform: translateY(100%);
transition: transform, opacity .3s;
opacity: 0;
// border-top-left-radius: 16px;
// border-top-right-radius: 16px;
&::before{
position: absolute;
width: 100%;
height: 100%;
content: '';
background: rgba(0,0,0,.6);
}
.text-case-list{
position: absolute;
width: 100%;
bottom: 0;
top: 150px;
background:rgba(1,14,31,1);
border-top-left-radius: 16px;
border-top-right-radius: 16px;
border:2px solid rgba(33,56,87,1);
color: #fff;
}
.list-header{
display: flex;
justify-content: space-between;
align-items: center;
height: 88px;
padding: 30px;
box-sizing: border-box;
border-bottom: 2px solid #213857;
.header-title{
color:#637DA6;
// font-size: 24px;
font-size: 48px;
}
.header-close{
// font-size: 42px;
font-size: 48px;
color: #2EA4FF;
}
}
.wxcode-test-result{
// display: flex;
display: none;
height: 72px;
// background: gold;
align-items: center;
padding: 0 30px;
.success{
color: rgba(68,209,95,1);
}
.fail{
color: rgba(196, 79, 78, 1);
}
.icon{
font-size: 48px !important;
position: relative;
// top: -8px;
margin-right: 10px;
}
.result-txt{
// font-size: 34px;
font-size: 48px;
}
.result-txt-desc{
max-width: 500px;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
margin: 20px;
}
&.active{
display: flex;;
}
}
.case-list{
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0px;
overflow: auto;
margin-top: 88px;
padding: 0 30px 30px;
// margin: 88px 30px 0;
.case-item{
position: relative;
margin-top: 30px;
border-radius: 8px;
background:rgba(23,39,64,1);
padding: 30px;
}
&.hasResult{
margin-top: 170px;
.case-item:first-child{
margin-top: 0;
}
}
.case-item-header{
display: flex;
justify-content: space-between;
align-items: center;
.case_item_success,
.case_item_fail{
font-size: 24px !important;
transform: scale(2);
}
.case_item_success{
color: rgba(68,209,95,1);
}
.case_item_fail{
color: rgba(196, 79, 78, 1);
}
.case-item-tips{
color: #C67676;
font-size: 38px;
}
}
.item-header-desc{
font-size: 48px;
color: #405D8C;
line-height: 1.5;
// background: gold;
.icon{
position: relative;
top: -4px;
font-size: 42px !important;
margin-right: 10px;
}
&.active{
color: #fff;
}
}
.case-item-tips{
font-size: 42px;
}
.case-item-desc{
display: none;
flex-direction: column;
font-size: 42px;
line-height: 1.5;
&.active{
display: flex;
}
}
.desc-title{
color: #637DA6;
line-height: 2;
font-size: 48px;
}
.text-area-style{
background:#010E1F !important;
color: #fff;
font-size: 48px;
line-height: 1.5;
border: none;
}
}
&.active{
transform: translateY(0);
opacity: 1;
// .item-header-desc{
// color: #fff;
// }
}
}
.code-evaluate-loading,
.pass-dialog{
display: none;
position: fixed;
left: 0;
right: 0;
bottom: 0;
top: 0;
justify-content: center;
align-items: center;
&::before{
position: absolute;
width: 100%;
height: 100%;
content: '';
background: rgba(0,0,0,.5);
}
&.active{
display: flex;
}
}
.code-evaluate-loading{
// display: none;
.loading-flex{
display: flex;
position: absolute;
flex-direction: column;
justify-content: center;
width: 100%;
height: 100%;
text-align: center;
color: #fff;
.loading-icon{
font-size: 100px !important;
}
.loading-txt{
font-size: 42px;
}
}
}
.pass-dialog{
flex-direction: column;
.pass-box{
position: relative;
z-index: 10;
background: #fff;
border-radius: 15px;
width: 490px;
height: 490px;
overflow: hidden;
}
.pass-img{
height: 200px;
background: url(../../images/wx-head.png) center center no-repeat;
// background: url(../../../public/images/wx-head.png) center center no-repeat;
}
.pass-ctx{
display: flex;
flex-direction: column;
// justify-content: center;
justify-content: space-between;
align-items: center;
height: 290px;
padding: 40px 0;
.pass-title,
.pass-value,
.pass-btn .btn,
.pass-btn-all .btn{
font-size: 42px;
line-height: 1.5;
}
.pass-value{
color: #888888;
.value_color{
color: rgb(234, 185, 35);
}
}
.pass-btn,
.pass-btn-all{
display: flex;
justify-content: space-between;
// background: gold;
padding: 0 40px;
width: 100%;
margin-top: 10px;
}
.pass-btn-all{
justify-content: center;
}
.btn{
outline: none;
border-radius: 999px;
padding: 0 30px;
height: 70px;
&.btn-first{
border: 1px solid #2EA4FF;
color: #2EA4FF;
}
&.btn-second{
background-color: #2EA4FF;
color: #fff;
}
}
.pass-title{
color: #2EA4FF;
}
}
}
.icon_close{
display: block;
position: relative;
z-index: 10;
color: #555;
font-size: 100px!important;
}
}

@ -92,7 +92,16 @@ const types = {
/** 统计 */ /** 统计 */
GET_STATIC_INFO: 'GET_STATIC_INFO', GET_STATIC_INFO: 'GET_STATIC_INFO',
CHANGE_STATIC_PARAMS: 'CHANGE_STATIC_PARAMS', CHANGE_STATIC_PARAMS: 'CHANGE_STATIC_PARAMS',
CHANGE_STATIC_TOTAL: 'CHANGE_STATIC_TOTAL' CHANGE_STATIC_TOTAL: 'CHANGE_STATIC_TOTAL',
/** 微信 */
GET_WXCODE_BY_IDENTIFIER: 'GET_WXCODE_BY_IDENTIFIER',
GET_WXCODE_TEST_CASE: 'GET_WXCODE_TEST_CASE',
UPDATE_WXCODE_BY_IDENTIFIER: 'UPDATE_WXCODE_BY_IDENTIFIER',
UPDATE_WXCODE_FOR_EDITOR: 'UPDATE_WXCODE_FOR_EDITOR',
IS_SHOW_WXCODE_TEST_CASES: 'IS_SHOW_WXCODE_TEST_CASES',
SHOW_WX_CODE_LOADING: 'SHOW_WX_CODE_LOADING',
SHOW_WX_CODE_DIALOG: 'SHOW_WX_CODE_DIALOG',
SET_GOLD_AND_EXPERIENCE: 'SET_GOLD_AND_EXPERIENCE'
} }
export default types; export default types;

@ -12,6 +12,7 @@ import {
changePaginationInfo, changePaginationInfo,
deleteItem deleteItem
} from './ojList'; } from './ojList';
import { import {
validateOjForm, validateOjForm,
saveOjFormCode, saveOjFormCode,
@ -109,6 +110,17 @@ import {
initTotal initTotal
} from './static'; } from './static';
import {
getWXCode,
getWXCodeTestCase,
restoreWXCode,
updateWXCodeForEditor,
updateWXCodeForInterval,
evaluateWxCode,
showWXCodeTextCase,
changeWXCodeEvaluateLoading,
changeWXCodeEvaluateDialog
} from './wxCode';
export default { export default {
toggleTodo, toggleTodo,
getOJList, getOJList,
@ -191,5 +203,15 @@ export default {
// 统计 // 统计
staticList, staticList,
changeParams, changeParams,
initTotal initTotal,
// 微信
getWXCode,
getWXCodeTestCase,
restoreWXCode,
updateWXCodeForEditor,
updateWXCodeForInterval,
evaluateWxCode,
showWXCodeTextCase,
changeWXCodeEvaluateLoading,
changeWXCodeEvaluateDialog
} }

@ -0,0 +1,246 @@
/*
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2020-01-15 15:41:10
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-18 11:43:43
*/
import types from './actionTypes.js';
import {
fetchWxCode,
fetchWxCodeTextCase,
fetchRestoreWxCode,
fetchUpdateWxCode,
fetchWxCodeGameBuild,
fetchWxCodeGameStatus
} from '../../services/wxcodeService.js';
// 加载代码块
export const getWXCode = (identifier) => {
return (dispatch) => {
fetchWxCode(identifier).then(res => {
if (res.status === 200) {
dispatch({
type: types.GET_WXCODE_BY_IDENTIFIER,
payload: res.data.content
});
}
});
}
};
// 加载测试集
export const getWXCodeTestCase = (identifier, params) => {
return (dispatch) => {
fetchWxCodeTextCase(identifier, params).then(res => {
// console.log('加载测试集: ====>>>>>>', res);
try{
const {data = {}} = res;
console.log(data.test_sets);
const _path = data.challenge.path;
dispatch({
type: types.GET_WXCODE_TEST_CASE,
payload: {
test_sets: data.test_sets || [],
game_id: data.game && data.game.id,
myIdentifier: data.myshixun.identifier,
exec_time: data.challenge.exec_time,
path: _path.split('')[0] || _path.split(';')[0] || _path,
last_compile_output: data.last_compile_output,
test_sets_count: data.test_sets_count,
sets_error_count: data.sets_error_count
}
});
} catch(err) {
console.log(err);
};
});
}
}
// 初始化
export const restoreWXCode = (identifier, params) => {
return (dispatch) => {
fetchRestoreWxCode(identifier, params).then(res => {
console.log('点击了初始化代码: ', res);
const {data} = res;
dispatch({
type: types.SHOW_WX_CODE_LOADING,
payload: false
})
dispatch({
type: types.GET_WXCODE_BY_IDENTIFIER,
payload: data.content || ''
});
});
}
}
// 更新编辑器代码
export const updateWXCodeForEditor = (code) => {
return {
type: types.UPDATE_WXCODE_FOR_EDITOR,
payload: code
}
}
export const updateWxCode = (path, identifier, userCode, game_id, evaluate = 0,) => {
return fetchUpdateWxCode(identifier, {
path,
evaluate,
content: userCode,
game_id
});
}
// 定时更新代码内容
export const updateWXCodeForInterval = (identifier, $path) => {
return (dispatch, getState) => {
const {wxCode, userCode, game_id, myIdentifier, path} = getState().wxcodeReducer;
if (wxCode !== userCode) {
updateWxCode(path, myIdentifier, userCode, game_id, 0).then(res => {
dispatch({
type: types.GET_WXCODE_BY_IDENTIFIER,
payload: userCode
});
});
}
}
}
// 评测
export const evaluateWxCode = (identifier, path) => {
return (dispatch, getState) => {
const {
userCode,
wxCode,
game_id,
myIdentifier,
exec_time,
path,
last_compile_output,
test_sets_count,
sets_error_count
} = getState().wxcodeReducer;
updateWxCode(path, myIdentifier, userCode, game_id, 1).then(res => {
// build
// const {} = res;
console.log(res);
const _resubmit = res.data.resubmit;
const params = {
first: 1,
content_modified: userCode !== wxCode ? 1 : 0,
sec_key: res.data.sec_key,
resubmit: _resubmit
}
setTimeout(() => {
// console.log(params);
fetchWxCodeGameBuild(identifier, params).then(res => {
const {status} = res.data;
if (status === 1) {
// 定时调用 game_status fetchWxCodeGameStatus
let count = 1;
const intervalTime = 500;
let time_out = false;
function wxCodeGameStatus (intervalTime, finalTime, count, timer) {
const excuteTime = (count++) * intervalTime; // 当前执行时间
console.log(finalTime, count, excuteTime);
if ((excuteTime / 1000) > (finalTime + 1)) time_out = true;
fetchWxCodeGameStatus(identifier, {resubmit: _resubmit, time_out}).then(r => {
const { status, test_sets = [], gold, experience, next_game, sets_error_count, test_sets_count, last_compile_output} = r.data;
if (+status > -1 || ((excuteTime / 1000) > (finalTime + 1))) {
clearInterval(timer);
timer = null;
dispatch({
type: types.SHOW_WX_CODE_LOADING,
payload: false
});
setTimeout(() => {
// 显示测试集弹框
// dispatch({
// type: types.IS_SHOW_WXCODE_TEST_CASES,
// payload: true
// });
// 评测是否通过, 通过 弹通过,否则 弹测试集
if (status === 2 && sets_error_count === 0) {
dispatch({
type: types.SET_GOLD_AND_EXPERIENCE,
payload: {
gold,
experience,
next_game
}
});
dispatch({
type: types.SHOW_WX_CODE_DIALOG,
payload: true
});
} else {
dispatch({
type: types.IS_SHOW_WXCODE_TEST_CASES,
payload: true
});
}
dispatch({
type: types.GET_WXCODE_TEST_CASE,
payload: {
test_sets,
game_id,
myIdentifier,
exec_time,
path,
last_compile_output,
test_sets_count,
sets_error_count
}
});
}, 50);
}
}).catch(err => {
dispatch({
type: types.SHOW_WX_CODE_LOADING,
payload: false
});
});
}
let timer = setInterval(() => {
wxCodeGameStatus(intervalTime, exec_time, count++, timer);
}, intervalTime);
}
})
}, 50);
}).catch(err => {
dispatch({
type: types.SHOW_WX_CODE_LOADING,
payload: false
});
});
}
}
// 显示测试集
export const showWXCodeTextCase = (flag) => {
return {
type: types.IS_SHOW_WXCODE_TEST_CASES,
payload: flag
}
}
// 显示测评中的状态
export const changeWXCodeEvaluateLoading = (flag) => {
return {
type: types.SHOW_WX_CODE_LOADING,
payload: flag
}
}
// 关闭对话框
export const changeWXCodeEvaluateDialog = (flag) => {
return {
type: types.SHOW_WX_CODE_DIALOG,
payload: flag
}
}

@ -17,6 +17,7 @@ import jupyterReducer from './jupyterReducer';
import commentReducer from './commentReducer'; import commentReducer from './commentReducer';
import tpiReducer from './tpiReducer'; import tpiReducer from './tpiReducer';
import staticReducer from './staticReducer'; import staticReducer from './staticReducer';
import wxcodeReducer from './wxcodeReducer';
export default combineReducers({ export default combineReducers({
testReducer, testReducer,
@ -28,5 +29,6 @@ export default combineReducers({
jupyterReducer, jupyterReducer,
commentReducer, commentReducer,
tpiReducer, tpiReducer,
staticReducer staticReducer,
wxcodeReducer
}); });

@ -0,0 +1,85 @@
/*
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2020-01-15 15:37:44
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-18 09:46:04
*/
import types from "../actions/actionTypes";
const initialState = {
wxCode: '',
userCode: '',
testCase: [],
game_id: '',
myIdentifier: '',
exec_time: 0,
last_compile_output: '',
test_sets_count: 0,
sets_error_count: 0,
path: '',
isShow: false,
showLoading: false,
showDialog: false,
gold: 0,
experience: 0,
next_game: ''
};
const wxcodeReducer = (state = initialState, action) => {
const { payload, type } = action;
switch (type) {
case types.GET_WXCODE_BY_IDENTIFIER:
console.log('=====>>>>>', payload);
return {
...state,
wxCode: payload,
userCode: payload
}
case types.GET_WXCODE_TEST_CASE:
return {
...state,
testCase: payload.test_sets,
game_id: payload.game_id,
myIdentifier: payload.myIdentifier,
exec_time: payload.exec_time,
path: payload.path,
last_compile_output: payload.last_compile_output,
test_sets_count: payload.test_sets_count,
sets_error_count: payload.sets_error_count
}
case types.UPDATE_WXCODE_FOR_EDITOR:
return {
...state,
userCode: payload
}
case types.IS_SHOW_WXCODE_TEST_CASES:
return {
...state,
isShow: payload
}
case types.SHOW_WX_CODE_LOADING:
return {
...state,
showLoading: payload
}
case types.SHOW_WX_CODE_DIALOG:
return {
...state,
showDialog: payload
}
case types.SET_GOLD_AND_EXPERIENCE:
return {
...state,
gold: payload.gold,
experience: payload.experience,
next_game: payload.next_game
}
default:
return {
...state
}
}
}
export default wxcodeReducer;

@ -0,0 +1,75 @@
/*
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2020-01-15 15:44:36
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-17 20:39:13
*/
import axios from 'axios';
import cookie from 'react-cookies'
// axios.defaults.withCredentials = true;
const setCookier = () => {
const _params = window.location.search;
if (_params) {
let _search = _params.split('?')[1];
_search.split('&').forEach(item => {
// console.log(item);
const _arr = item.split('=');
cookie.remove(_arr[0], {
path: '/',
domain: '.educoder.net'
});
cookie.save(_arr[0], _arr[1], { domain: '.educoder.net', path: '/'});
});
}
}
// 获取代码块
export async function fetchWxCode (identifier, params) {
setCookier();
const url = `/tasks/${identifier}/rep_content.json`;
params = Object.assign({}, params, {withCredentials: true});
return axios.get(url, {params});
}
// 获取测试值
export async function fetchWxCodeTextCase (identifier) {
setCookier();
const url = `/tasks/${identifier}.json`;
const params = Object.assign({}, {withCredentials: true});
return axios.get(url, {params});
}
// 更新代码块内容
export async function fetchUpdateWxCode (identifier, params) {
setCookier();
// /myshixuns/8etu3pilsa/update_file.json
const url = `/myshixuns/${identifier}/update_file.json`;
params = Object.assign({}, params, {withCredentials: true});
return axios.post(url, params);
}
// 恢复初始化
export async function fetchRestoreWxCode (identifier, params) {
setCookier();
const url = `/tasks/${identifier}/reset_original_code.json`;
params = Object.assign({}, params, {withCredentials: true});
return axios.get(url, {params});
}
// 评测
export async function fetchWxCodeGameBuild (identifier, params) {
setCookier();
const url = `/tasks/${identifier}/game_build.json`;
params = Object.assign({}, params, {withCredentials: true});
return axios.get(url, {params});
}
export async function fetchWxCodeGameStatus (identifier, params) {
setCookier();
const url = `/tasks/${identifier}/game_status.json`;
params = Object.assign({}, params, {withCredentials: true});
return axios.get(url, {params});
}

@ -1134,6 +1134,12 @@
<div class="code-name">&amp;#xe7f9;</div> <div class="code-name">&amp;#xe7f9;</div>
</li> </li>
<li class="dib">
<span class="icon iconfont">&#xe71b;</span>
<div class="name">过滤器</div>
<div class="code-name">&amp;#xe71b;</div>
</li>
<li class="dib"> <li class="dib">
<span class="icon iconfont">&#xe6ee;</span> <span class="icon iconfont">&#xe6ee;</span>
<div class="name">20从属连接</div> <div class="name">20从属连接</div>
@ -1866,6 +1872,18 @@
<div class="code-name">&amp;#xe719;</div> <div class="code-name">&amp;#xe719;</div>
</li> </li>
<li class="dib">
<span class="icon iconfont">&#xe71c;</span>
<div class="name">初始化</div>
<div class="code-name">&amp;#xe71c;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe71e;</span>
<div class="name">测试集</div>
<div class="code-name">&amp;#xe71e;</div>
</li>
</ul> </ul>
<div class="article markdown"> <div class="article markdown">
<h2 id="unicode-">Unicode 引用</h2> <h2 id="unicode-">Unicode 引用</h2>
@ -3572,6 +3590,15 @@
</div> </div>
</li> </li>
<li class="dib">
<span class="icon iconfont icon-guolvqi"></span>
<div class="name">
过滤器
</div>
<div class="code-name">.icon-guolvqi
</div>
</li>
<li class="dib"> <li class="dib">
<span class="icon iconfont icon-congshulianjie"></span> <span class="icon iconfont icon-congshulianjie"></span>
<div class="name"> <div class="name">
@ -4670,6 +4697,24 @@
</div> </div>
</li> </li>
<li class="dib">
<span class="icon iconfont icon-chushihua"></span>
<div class="name">
初始化
</div>
<div class="code-name">.icon-chushihua
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-ceshiji"></span>
<div class="name">
测试集
</div>
<div class="code-name">.icon-ceshiji
</div>
</li>
</ul> </ul>
<div class="article markdown"> <div class="article markdown">
<h2 id="font-class-">font-class 引用</h2> <h2 id="font-class-">font-class 引用</h2>
@ -6171,6 +6216,14 @@
<div class="code-name">#icon-gengduo1</div> <div class="code-name">#icon-gengduo1</div>
</li> </li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-guolvqi"></use>
</svg>
<div class="name">过滤器</div>
<div class="code-name">#icon-guolvqi</div>
</li>
<li class="dib"> <li class="dib">
<svg class="icon svg-icon" aria-hidden="true"> <svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-congshulianjie"></use> <use xlink:href="#icon-congshulianjie"></use>
@ -7147,6 +7200,22 @@
<div class="code-name">#icon-jiashang1</div> <div class="code-name">#icon-jiashang1</div>
</li> </li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-chushihua"></use>
</svg>
<div class="name">初始化</div>
<div class="code-name">#icon-chushihua</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-ceshiji"></use>
</svg>
<div class="name">测试集</div>
<div class="code-name">#icon-ceshiji</div>
</li>
</ul> </ul>
<div class="article markdown"> <div class="article markdown">
<h2 id="symbol-">Symbol 引用</h2> <h2 id="symbol-">Symbol 引用</h2>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1293,6 +1293,13 @@
"unicode": "e7f9", "unicode": "e7f9",
"unicode_decimal": 59385 "unicode_decimal": 59385
}, },
{
"icon_id": "5327531",
"name": "过滤器",
"font_class": "guolvqi",
"unicode": "e71b",
"unicode_decimal": 59163
},
{ {
"icon_id": "5379378", "icon_id": "5379378",
"name": "20从属连接", "name": "20从属连接",
@ -2146,6 +2153,20 @@
"font_class": "jiashang1", "font_class": "jiashang1",
"unicode": "e719", "unicode": "e719",
"unicode_decimal": 59161 "unicode_decimal": 59161
},
{
"icon_id": "12826208",
"name": "初始化",
"font_class": "chushihua",
"unicode": "e71c",
"unicode_decimal": 59164
},
{
"icon_id": "12826211",
"name": "测试集",
"font_class": "ceshiji",
"unicode": "e71e",
"unicode_decimal": 59166
} }
] ]
} }

@ -572,6 +572,9 @@ Created by iconfont
<glyph glyph-name="gengduo1" unicode="&#59385;" d="M104 376m-104 0a104 104 0 1 1 208 0 104 104 0 1 1-208 0ZM920 376m-104 0a104 104 0 1 1 208 0 104 104 0 1 1-208 0ZM512 384m-160 0a160 160 0 1 1 320 0 160 160 0 1 1-320 0Z" horiz-adv-x="1024" /> <glyph glyph-name="gengduo1" unicode="&#59385;" d="M104 376m-104 0a104 104 0 1 1 208 0 104 104 0 1 1-208 0ZM920 376m-104 0a104 104 0 1 1 208 0 104 104 0 1 1-208 0ZM512 384m-160 0a160 160 0 1 1 320 0 160 160 0 1 1-320 0Z" horiz-adv-x="1024" />
<glyph glyph-name="guolvqi" unicode="&#59163;" d="M908.6 761.3c6.5-15.5 3.8-28.8-8-39.7L620.9 441.9V21c0-15.8-7.4-27.1-22.1-33.5-4.9-2-9.7-2.9-14.2-2.9-10.3 0-18.7 3.6-25.5 10.7L413.8 140.8c-7.2 7.2-10.7 15.7-10.7 25.5V441.9L123.4 721.6c-11.7 11-14.3 24.2-8 39.7 6.5 14.8 17.5 22.1 33.5 22.1H875c16 0.1 27.2-7.3 33.6-22.1z" horiz-adv-x="1024" />
<glyph glyph-name="congshulianjie" unicode="&#59118;" d="M844.8 230.4a128 128 0 0 1-125.44-102.4H358.4a102.4 102.4 0 0 0 0 204.8h307.2a153.6 153.6 0 0 1 0 307.2H304.64a128 128 0 1 1 0-51.2H665.6a102.4 102.4 0 0 0 0-204.8H358.4a153.6 153.6 0 0 1 0-307.2h360.96a128 128 0 1 1 125.44 153.6z m0-204.8a76.8 76.8 0 1 0 76.8 76.8 76.8 76.8 0 0 0-76.8-76.8z" horiz-adv-x="1024" /> <glyph glyph-name="congshulianjie" unicode="&#59118;" d="M844.8 230.4a128 128 0 0 1-125.44-102.4H358.4a102.4 102.4 0 0 0 0 204.8h307.2a153.6 153.6 0 0 1 0 307.2H304.64a128 128 0 1 1 0-51.2H665.6a102.4 102.4 0 0 0 0-204.8H358.4a153.6 153.6 0 0 1 0-307.2h360.96a128 128 0 1 1 125.44 153.6z m0-204.8a76.8 76.8 0 1 0 76.8 76.8 76.8 76.8 0 0 0-76.8-76.8z" horiz-adv-x="1024" />
@ -938,6 +941,12 @@ Created by iconfont
<glyph glyph-name="jiashang1" unicode="&#59161;" d="M512-128C228.693333-128 0 100.693333 0 384S228.693333 896 512 896s512-228.693333 512-512-228.693333-512-512-512z m0 989.866667C249.173333 861.866667 34.133333 646.826667 34.133333 384s215.04-477.866667 477.866667-477.866667 477.866667 215.04 477.866667 477.866667S774.826667 861.866667 512 861.866667zM699.733333 332.8h-375.466666c-20.48 0-34.133333 13.653333-34.133334 34.133333s13.653333 34.133333 34.133334 34.133334h375.466666c20.48 0 34.133333-13.653333 34.133334-34.133334s-13.653333-34.133333-34.133334-34.133333zM512 145.066667c-20.48 0-34.133333 13.653333-34.133333 34.133333V554.666667c0 20.48 13.653333 34.133333 34.133333 34.133333s34.133333-13.653333 34.133333-34.133333v-375.466667c0-20.48-13.653333-34.133333-34.133333-34.133333z" horiz-adv-x="1024" /> <glyph glyph-name="jiashang1" unicode="&#59161;" d="M512-128C228.693333-128 0 100.693333 0 384S228.693333 896 512 896s512-228.693333 512-512-228.693333-512-512-512z m0 989.866667C249.173333 861.866667 34.133333 646.826667 34.133333 384s215.04-477.866667 477.866667-477.866667 477.866667 215.04 477.866667 477.866667S774.826667 861.866667 512 861.866667zM699.733333 332.8h-375.466666c-20.48 0-34.133333 13.653333-34.133334 34.133333s13.653333 34.133333 34.133334 34.133334h375.466666c20.48 0 34.133333-13.653333 34.133334-34.133334s-13.653333-34.133333-34.133334-34.133333zM512 145.066667c-20.48 0-34.133333 13.653333-34.133333 34.133333V554.666667c0 20.48 13.653333 34.133333 34.133333 34.133333s34.133333-13.653333 34.133333-34.133333v-375.466667c0-20.48-13.653333-34.133333-34.133333-34.133333z" horiz-adv-x="1024" />
<glyph glyph-name="chushihua" unicode="&#59164;" d="M511.682434-128A468.457376 468.457376 0 0 0 44.559694 340.457376a66.73182 66.73182 0 0 0 133.46364 0A333.6591 333.6591 0 1 1 511.682434 675.451113a330.989827 330.989827 0 0 1-235.785764-98.095776 66.73182 66.73182 0 0 0-111.2197 66.73182l60.503517 203.309612a66.798552 66.798552 0 1 0 128.125094-37.814698l-9.564894-31.808835A468.234937 468.234937 0 1 0 511.682434-128zM600.658194 183.41516h-133.46364a66.73182 66.73182 0 0 0-66.73182 66.73182V472.58638a66.73182 66.73182 0 0 0 133.46364 0v-155.70758h66.73182a66.73182 66.73182 0 0 0 0-133.46364z" horiz-adv-x="1024" />
<glyph glyph-name="ceshiji" unicode="&#59166;" d="M536.332172 333.97898a75.330291 75.330291 0 0 0-29.985844 6.338471l-463.195967 207.95061a73.136205 73.136205 0 0 0 0 133.351681l463.195967 207.95061a73.136205 73.136205 0 0 0 59.971688 0l463.195966-207.95061a73.136205 73.136205 0 0 0 0-133.351681l-463.195966-207.95061a75.330291 75.330291 0 0 0-29.985844-6.338471zM251.832333 614.822008L536.332172 487.321224l284.499838 127.500784L536.332172 742.56658zM536.332172 102.868571a73.136205 73.136205 0 0 0-31.204781 7.069834l-463.195967 219.408615a73.184963 73.184963 0 0 0 62.409562 132.376532L536.332172 257.429752l431.259823 215.020443a73.136205 73.136205 0 0 0 65.33501-130.913807l-463.195966-230.866621a73.136205 73.136205 0 0 0-33.398867-7.801196zM536.332172-127.99805a73.136205 73.136205 0 0 0-31.204781 7.069833l-463.195967 219.408616a73.136205 73.136205 0 0 0 62.409562 131.888957L536.332172 26.319343l431.259823 215.264231a73.136205 73.136205 0 0 0 65.33501-130.913807l-463.195966-230.866621A73.136205 73.136205 0 0 0 536.332172-127.99805z" horiz-adv-x="1073" />
</font> </font>

Before

Width:  |  Height:  |  Size: 381 KiB

After

Width:  |  Height:  |  Size: 383 KiB

Loading…
Cancel
Save