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

dev_cs
杨树林 5 years ago
commit 1b52309182

@ -191,19 +191,26 @@ class ChallengesController < ApplicationController
sets_input = test_set.map(&:input) sets_input = test_set.map(&:input)
sets_open = test_set.map(&:is_public) sets_open = test_set.map(&:is_public)
set_score = test_set.map(&:score) set_score = test_set.map(&:score)
set_match_rule = test_set.map(&:match_rule)
params_hidden = params[:test_set].map{|set| set[:hidden].to_i == 0} params_hidden = params[:test_set].map{|set| set[:hidden].to_i == 0}
params_output = params[:test_set].map{|set| set[:output] } params_output = params[:test_set].map{|set| set[:output] }
params_input = params[:test_set].map{|set| set[:input] } params_input = params[:test_set].map{|set| set[:input] }
params_score = params[:test_set].map{|set| set[:score]} params_score = params[:test_set].map{|set| set[:score]}
params_test_set = params[:test_set].map{|set| set[:match_rule]}
# 测试集变化则需要更新(输入、 输出、 是否隐藏) # 测试集变化则需要更新(输入、 输出、 是否隐藏)
if sets_output != params_output || sets_open != params_hidden || sets_input != params_input || set_score != params_score if sets_output != params_output || sets_open != params_hidden || sets_input != params_input ||
set_score != params_score || params_test_set != set_match_rule
test_set.delete_all unless test_set.blank? test_set.delete_all unless test_set.blank?
params[:test_set].each_with_index do |set, index| params[:test_set].each_with_index do |set, index|
# last 末尾匹配, full: 全完匹配
logger.info("set: #{set}; match_rule : #{set[:match_rule]}")
match_rule = set[:match_rule] == 'last' ? 'last' : 'full'
TestSet.create(:challenge_id => @challenge.id, TestSet.create(:challenge_id => @challenge.id,
:input => "#{set[:input]}", :input => "#{set[:input]}",
:output => "#{set[:output]}", :output => "#{set[:output]}",
:is_public => params_hidden[index], :is_public => params_hidden[index],
:score => set[:score], :score => set[:score],
:match_rule => "#{match_rule}",
:position => (index + 1)) :position => (index + 1))
end end
@challenge.update_column(:modify_time, Time.now) @challenge.update_column(:modify_time, Time.now)

@ -537,10 +537,12 @@ class GamesController < ApplicationController
game_challenge.test_sets.each do |test_set| game_challenge.test_sets.each do |test_set|
input = test_set.input.nil? ? "" : test_set.input.gsub("\r\n", "\n") input = test_set.input.nil? ? "" : test_set.input.gsub("\r\n", "\n")
output = test_set.output.nil? ? "" : test_set.output.gsub("\r\n", "\n") output = test_set.output.nil? ? "" : test_set.output.gsub("\r\n", "\n")
test_cases = {:input => input, :output => output} test_cases = {:input => input, :output => output, :matchRule => test_set.match_rule}
testSet << test_cases testSet << test_cases
end end
logger.info("##############testSet: #{testSet}")
testCases = Base64.urlsafe_encode64(testSet.to_json) unless testSet.blank? testCases = Base64.urlsafe_encode64(testSet.to_json) unless testSet.blank?
# 评测类型: 012 用于webssh的评测 3用于vnc # 评测类型: 012 用于webssh的评测 3用于vnc
podType = @shixun.vnc_evaluate ? 3 : @shixun.webssh podType = @shixun.vnc_evaluate ? 3 : @shixun.webssh
@ -560,6 +562,15 @@ class GamesController < ApplicationController
# needPortMapping web类型需要pod端口映射 # needPortMapping web类型需要pod端口映射
br_params[:needPortMapping] = 8080 if @myshixun.mirror_name.include?("Web") br_params[:needPortMapping] = 8080 if @myshixun.mirror_name.include?("Web")
# 私密仓库的设置
secret_rep = @shixun.shixun_secret_repository
logger.info("############secret_rep: #{secret_rep}")
if secret_rep&.repo_name
secretGitUrl = repo_url secret_rep.repo_path
br_params.merge!({secretGitUrl: Base64.urlsafe_encode64(secretGitUrl), secretDir: secret_rep.secret_dir_path})
logger.info("#######br_params:#{br_params}")
end
# 中间层交互 # 中间层交互
uri = "#{shixun_tomcat}/bridge/game/gameEvaluate" uri = "#{shixun_tomcat}/bridge/game/gameEvaluate"
res = interface_post uri, br_params, 502, "gameEvaluate failed" res = interface_post uri, br_params, 502, "gameEvaluate failed"

@ -220,6 +220,16 @@ class ShixunsController < ApplicationController
evaluate_script: @shixun.evaluate_script) evaluate_script: @shixun.evaluate_script)
end end
# 同步私密版本库
if @shixun.shixun_secret_repository
repo_name = "#{current_user.login}/secret_#{@shixun.identifier}"
fork_repository_name = "#{current_user.login}/secret_#{@new_shixun.identifier}"
ShixunSecretRepository.create!(shixun_id: @new_shixun.id,
repo_name: "#{repo_name}",
secret_dir_path: @shixun.shixun_secret_repository.secret_dir_path)
GitService.fork_repository(repo_path: "#{repo_name}.git", fork_repository_path: (fork_repository_name + ".git"))
end
# 同步镜像 # 同步镜像
if @shixun.mirror_repositories.present? if @shixun.mirror_repositories.present?
@shixun.mirror_repositories.each do |mirror| @shixun.mirror_repositories.each do |mirror|
@ -473,8 +483,20 @@ class ShixunsController < ApplicationController
@shixun.shixun_service_configs.create!(config) if name.present? @shixun.shixun_service_configs.create!(config) if name.present?
end end
end end
# 添加第二仓库
if params[:is_secret_repository]
add_secret_repository
else
# 如果有仓库,就要删
if @shixun.shixun_secret_repository&.repo_name
@shixun.shixun_secret_repository.lock!
GitService.delete_repository(repo_path: @shixun.shixun_secret_repository.repo_path)
@shixun.shixun_secret_repository.destroy
end
end
rescue Exception => e rescue Exception => e
uid_logger_error(e.message) uid_logger_error("实训保存失败--------#{e.message}")
tip_exception("实训保存失败") tip_exception("实训保存失败")
raise ActiveRecord::Rollback raise ActiveRecord::Rollback
end end
@ -819,6 +841,30 @@ class ShixunsController < ApplicationController
end end
end end
# 设置私密版本库的在tpm中的目录
def set_secret_dir
raise("设置路径不能为空") if params[:secret_dir_path].blank?
raise("请先配置私密版本库") if @shixun.shixun_secret_repository.blank?
@shixun.shixun_secret_repository.update_attributes(:secret_dir_path => params[:secret_dir_path])
normal_status("设置成功")
end
def secret_repository
begin
@repo_path = @shixun.shixun_secret_repository&.repo_path
@repo_url = repo_url @repo_path
@trees = GitService.file_tree(repo_path: @repo_path, path: params[:path])
logger.info("#11@@#@#@#@111#@@@@###{@trees}")
if @trees
logger.info("#@@#@#@#@#@@@@###{@trees.try(:count)}")
@latest_commit = [GitService.commits(repo_path: @repo_path).first]
Rails.logger.info("########## #{@latest_commit}")
end
rescue Exception => e
logger.error(e.message)
end
end
include GitCommon include GitCommon
def update_file def update_file
@ -955,7 +1001,13 @@ private
end end
def find_repo_name def find_repo_name
@repo_path = @shixun.try(:repo_path) # 有私密版本库的参数时,需要拿私密仓库
@repo_path = if params[:secret_repository]
@shixun.shixun_secret_repository&.repo_path
else
@shixun.try(:repo_path)
end
logger.info("######{@repo_path}")
@path = params[:path] @path = params[:path]
end end
@ -990,4 +1042,13 @@ private
modify_shixun = ShixunModify.exists?(:myshixun_id => current_myshixun.id, :shixun_id => @shixun.id, :status => 1) modify_shixun = ShixunModify.exists?(:myshixun_id => current_myshixun.id, :shixun_id => @shixun.id, :status => 1)
games.size != min_challenges.size || modify_shixun games.size != min_challenges.size || modify_shixun
end end
# 添加私密仓库
def add_secret_repository
# 防止跟tpm版本库重名加了前缀secret
repo_path = repo_namespace(current_user.login, "secret_#{@shixun.identifier}")
GitService.add_repository(repo_path: repo_path)
ShixunSecretRepository.create!(repo_name: repo_path.split(".")[0], shixun_id: @shixun.id)
end
end end

@ -478,23 +478,31 @@ class StudentWorksController < ApplicationController
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
challenge = @homework.shixuns.first&.challenges.find_by(id: params[:challenge_id]) unless params[:challenge_id].blank? challenge = @homework.shixuns.first&.challenges.find_by(id: params[:challenge_id]) unless params[:challenge_id].blank?
if challenge.present? if challenge.present?
comment = @work.shixun_work_comments.find_by(challenge_id: challenge.id) || @comment = @work.shixun_work_comments.find_by(challenge_id: challenge.id) ||
ShixunWorkComment.new(student_work_id: @work.id, user_id: current_user.id, challenge_id: challenge.id) ShixunWorkComment.new(student_work_id: @work.id, user_id: current_user.id, challenge_id: challenge.id)
else else
comment = @work.shixun_work_comments.find_by(challenge_id: 0) || @comment = @work.shixun_work_comments.find_by(challenge_id: 0) ||
ShixunWorkComment.new(student_work_id: @work.id, user_id: current_user.id, challenge_id: 0) ShixunWorkComment.new(student_work_id: @work.id, user_id: current_user.id, challenge_id: 0)
end end
comment.comment = params[:comment] @comment.comment = params[:comment]
comment.hidden_comment = params[:hidden_comment] @comment.hidden_comment = params[:hidden_comment]
comment.save! @comment.save!
normal_status("评阅成功")
end end
end end
# 删除实训作品评阅 # 删除实训作品评阅
def destroy_work_comment def destroy_work_comment
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
@work.shixun_work_comments.find_by!(id: params[:comment_id]).destroy_all # tip_exception("visible_comment参数有误") if params[:visible_comment].nil?
comment = @work.shixun_work_comments.find_by!(id: params[:comment_id])
comment.destroy!
# params[:visible_comment] ? comment.comment = nil : comment.hidden_comment = nil
# if comment.comment.nil? && comment.hidden_comment.nil?
# comment.destroy!
# else
# comment.save!
# end
normal_status("删除成功") normal_status("删除成功")
end end
end end

@ -38,6 +38,9 @@ class Shixun < ApplicationRecord
has_one :shixun_info, dependent: :destroy has_one :shixun_info, dependent: :destroy
# 第二版本库
has_one :shixun_secret_repository, dependent: :destroy
belongs_to :user belongs_to :user
# 实训服务配置 # 实训服务配置
has_many :shixun_service_configs, :dependent => :destroy has_many :shixun_service_configs, :dependent => :destroy

@ -0,0 +1,11 @@
class ShixunSecretRepository < ApplicationRecord
# repo_name: 仓库名
# secret_dir_name: 在tpm仓库的那个目录下
belongs_to :shixun
def repo_path
"#{repo_name}.git"
end
end

@ -1,2 +1,3 @@
class TestSet < ApplicationRecord class TestSet < ApplicationRecord
# match_rule: 匹配规则: full 完全匹配, last 末尾匹配
end end

@ -16,7 +16,7 @@ elsif @tab == 1
json.has_web_route @shixun.has_web_route? json.has_web_route @shixun.has_web_route?
json.test_sets @challenge.test_sets do |set| json.test_sets @challenge.test_sets do |set|
json.hidden (set.is_public ? 0 : 1) json.hidden (set.is_public ? 0 : 1)
json.(set, :input, :output, :score) json.(set, :input, :output, :score, :match_rule)
end end
elsif @tab == 2 elsif @tab == 2
# 参考答案 # 参考答案

@ -6,6 +6,12 @@ json.results do
json.title highlights.delete(:name)&.join('...') || obj.searchable_title json.title highlights.delete(:name)&.join('...') || obj.searchable_title
# json.description highlights.values[0,5].each { |arr| arr.is_a?(Array) ? arr.join('...') : arr }.join('<br/>') # json.description highlights.values[0,5].each { |arr| arr.is_a?(Array) ? arr.join('...') : arr }.join('<br/>')
# 去除开头标点符号
reg = /^[,。?:;‘’!“”—……、]/
highlights[:description]&.first&.sub!(reg, '')
highlights[:content]&.first&.sub!(reg, '')
json.content highlights json.content highlights
end end
end end

@ -0,0 +1,8 @@
json.trees @trees
if @trees.present?
json.partial! 'shixuns/commit', locals: { commits: @latest_commit }
end
json.git_url @repo_url
json.secret_dir_path @shixun.shixun_secret_repository&.secret_dir_path

@ -30,6 +30,8 @@ json.shixun do
json.scope_partment @shixun.schools.map(&:name) # 公开范围 json.scope_partment @shixun.schools.map(&:name) # 公开范围
json.opening_time @shixun.opening_time json.opening_time @shixun.opening_time
json.forbid_copy @shixun.forbid_copy json.forbid_copy @shixun.forbid_copy
# 私密仓库
json.is_secret_repository @shixun.shixun_secret_repository.present?
# 实训服务配置 # 实训服务配置
json.shixun_service_configs do json.shixun_service_configs do

@ -2,3 +2,4 @@ json.fork_from @fork_from
json.identity User.current.shixun_identity(@shixun) json.identity User.current.shixun_identity(@shixun)
json.power @power json.power @power
json.partial! 'shixuns/top', locals: { shixun: @shixun, current_myshixun: @current_myshixun } json.partial! 'shixuns/top', locals: { shixun: @shixun, current_myshixun: @current_myshixun }
json.secret_repository @shixun.shixun_secret_repository.present?

@ -0,0 +1,3 @@
json.comment_id @comment.id
json.status 0
json.message "评阅成功"

@ -39,6 +39,7 @@ if @shixun
challenge_comment = @work.shixun_work_comments.find_by(challenge_id: game.challenge_id) challenge_comment = @work.shixun_work_comments.find_by(challenge_id: game.challenge_id)
json.challenge_comment challenge_comment&.comment json.challenge_comment challenge_comment&.comment
json.challenge_comment_hidden challenge_comment&.hidden_comment json.challenge_comment_hidden challenge_comment&.hidden_comment
json.comment_id challenge_comment&.id
end end
end end
@ -59,6 +60,7 @@ if @shixun
# 评阅信息 # 评阅信息
json.work_comment @comment&.comment json.work_comment @comment&.comment
json.work_comment_hidden @user_course_identity < Course::STUDENT ? @comment&.hidden_comment : nil json.work_comment_hidden @user_course_identity < Course::STUDENT ? @comment&.hidden_comment : nil
json.comment_id @comment&.id
# 图形统计 # 图形统计
# 1 效率 # 1 效率

@ -197,6 +197,8 @@ Rails.application.routes.draw do
get :get_script_contents get :get_script_contents
get :get_custom_script get :get_custom_script
post :repository post :repository
post :secret_repository
post :set_secret_dir
post :commits post :commits
post :file_content post :file_content
post :update_file post :update_file

@ -0,0 +1,10 @@
class CreateShixunSecretRepositories < ActiveRecord::Migration[5.2]
def change
create_table :shixun_secret_repositories do |t|
t.references :shixun
t.string :repo_name
t.string :secret_dir_path
t.timestamps
end
end
end

@ -0,0 +1,7 @@
class AddMatchRuleForTestSets < ActiveRecord::Migration[5.2]
def change
add_column :test_sets, :match_rule, :string
TestSet.update_all(match_rule: 'full')
end
end

@ -10,7 +10,6 @@ class AppraiseModal extends Component{
group_ids:[], group_ids:[],
fileList:[], fileList:[],
Inputsval:undefined, Inputsval:undefined,
valuetype:0,
textareavaltype:false, textareavaltype:false,
comment:undefined, comment:undefined,
hidden_comment:undefined hidden_comment:undefined
@ -18,11 +17,23 @@ class AppraiseModal extends Component{
} }
componentDidMount() { componentDidMount() {
let{data,work_comment,work_comment_hidden}=this.props;
if(this.props.showAppraisetype==="child"){
data.stage_list.map((item,key)=>{
if(this.props.challenge_id===item.challenge_id){
this.setState({ this.setState({
valuetype:this.props.work_type===undefined?0:this.props.work_type, comment:item.challenge_comment,
textareaval:this.props.work_comment, hidden_comment:item.challenge_comment_hidden,
}) })
}
})
}else{
this.setState({
comment:work_comment,
hidden_comment:work_comment_hidden,
})
}
} }
@ -38,11 +49,10 @@ class AppraiseModal extends Component{
}) })
} }
Saves=()=>{ Saves=()=>{
let{textareaval,valuetype,comment,hidden_comment}=this.state; let{comment,hidden_comment}=this.state;
let commenttype=comment===undefined||comment===null||comment===""; let commenttype=comment===undefined||comment===null||comment==="";
let hidden_commenttype=hidden_comment===undefined||hidden_comment===null||hidden_comment===""; let hidden_commenttype=hidden_comment===undefined||hidden_comment===null||hidden_comment==="";
if(commenttype===true&&hidden_commenttype===true){ if(commenttype===true&&hidden_commenttype===true){
this.setState({ this.setState({
textareavaltype:true textareavaltype:true
@ -50,20 +60,20 @@ class AppraiseModal extends Component{
return return
} }
//comment 是 text 可见的评阅内容 //comment 是 text 可见的评阅内容
// hidden_comment 是 text 不可见的评阅内容 // hidden_comment 是 text 不可见的评阅内容
// challenge_id 否 int 关卡id关卡评阅才需传关卡id // challenge_id 否 int 关卡id关卡评阅才需传关卡id
let challenge_id=this.props.showAppraisetype==="child"?this.props.challenge_id:undefined
let url=`/student_works/${this.props.match.params.homeworkid}/shixun_work_comment.json` let url=`/student_works/${this.props.match.params.homeworkid}/shixun_work_comment.json`
axios.post(url, { axios.post(url, {
comment:textareaval, comment:comment,
hidden_comment:valuetype hidden_comment:hidden_comment,
challenge_id:challenge_id
}).then((response) => { }).then((response) => {
if(response.data.status===0){ if(response.data.status===0){
this.props.showCancel(comment,hidden_comment,challenge_id,response.data.comment_id)
this.props.showNotification(response.data.message) this.props.showNotification(response.data.message)
this.props.showCancel(textareaval,valuetype)
}else{ }else{
this.props.showNotification(response.data.message) this.props.showNotification(response.data.message)
} }

@ -11,6 +11,7 @@ import Coursesshixundetails from './shixunreport/Coursesshixundetails';
import Shixunechart from './shixunreport/Shixunechart'; import Shixunechart from './shixunreport/Shixunechart';
import DownloadMessageysl from "../../modals/DownloadMessageysl" import DownloadMessageysl from "../../modals/DownloadMessageysl"
import AppraiseModal from "../coursesPublic/AppraiseModal"; import AppraiseModal from "../coursesPublic/AppraiseModal";
import ShowAppraiseList from './ShowAppraiseList';
import {UnControlled as CodeMirror} from 'react-codemirror2'; import {UnControlled as CodeMirror} from 'react-codemirror2';
import 'codemirror/mode/cmake/cmake'; import 'codemirror/mode/cmake/cmake';
import 'codemirror/mode/xml/xml'; import 'codemirror/mode/xml/xml';
@ -34,9 +35,8 @@ class ShixunWorkReport extends Component {
DownloadMessageval:undefined, DownloadMessageval:undefined,
isspinning:false, isspinning:false,
showAppraiseModaltype:false, showAppraiseModaltype:false,
work_comment_hidden:false, work_comment_hidden:undefined,
showAppraiseModalsshow:true, work_comment:undefined,
work_comment:null
} }
} }
@ -102,7 +102,7 @@ class ShixunWorkReport extends Component {
let homeworkid=this.props.match.params.homeworkid; let homeworkid=this.props.match.params.homeworkid;
let url = `/student_works/${homeworkid}/shixun_work_report.json` let url = `/student_works/${homeworkid}/shixun_work_report.json`
axios.get(url).then((result) => { axios.get(url).then((result) => {
if (result.data.status === 403||result.data.status === 401||result.data.status === 407||result.data.status === 408||result.data.status === 409) { if (result.data.status === 403 || result.data.status === 401 || result.data.status === 407 || result.data.status === 408|| result.data.status === 409 || result.data.status === 500) {
}else{ }else{
this.setState({ this.setState({
@ -155,76 +155,141 @@ class ShixunWorkReport extends Component {
}) })
} }
showAppraiseModal=(type,id)=>{ showAppraiseModal=(type,id,show,hidden)=>{
let{data}=this.state;
if(type==="child"){ if(type==="child"){
data.stage_list.forEach((item,key)=>{
if(item.challenge_id===id){
item.challenge_comment=show;
item.challenge_comment_hidden=hidden;
}
})
this.setState({ this.setState({
showAppraiseModaltype:true,
showAppraisetype:type, showAppraisetype:type,
challenge_id:id challenge_id:id,
data:data
}) })
}else{ }else{
this.setState({ this.setState({
showAppraiseModaltype:true,
showAppraisetype:type, showAppraisetype:type,
challenge_id:undefined challenge_id:undefined,
work_comment:show,
work_comment_hidden:hidden
}) })
} }
let{work_comment,work_comment_hidden}=this.state;
this.setState({
showAppraiseModaltype:true,
work_comment_hidden:work_comment===null||work_comment===undefined?this.state.work_type?true:false:work_comment_hidden,
})
} }
hideAppraiseModal=()=>{ hideAppraiseModal=()=>{
let{work_comment,work_comment_hidden}=this.state;
this.setState({ this.setState({
showAppraiseModaltype:false, showAppraiseModaltype:false,
work_comment_hidden:work_comment===null||work_comment===undefined?this.state.work_type===1?true:false:work_comment_hidden,
}) })
} }
showAppraiseModals=(list,type)=>{ showAppraiseModals=(show,hidden,id,comment_id)=>{
let{data,showAppraisetype}=this.state;
if(showAppraisetype==="child"){
data.stage_list.forEach((item,key)=>{
if(item.challenge_id===id){
item.challenge_comment=show;
item.challenge_comment_hidden=hidden;
item.comment_id=comment_id
}
})
this.setState({ this.setState({
showAppraiseModaltype:false, showAppraiseModaltype:false,
work_comment_hidden:type===0?false:true, data:data
work_comment:list,
work_type:type,
showAppraiseModals:true,
showAppraiseModalsshow:true,
}) })
}else{
data.comment_id=comment_id;
this.setState({
showAppraiseModaltype:false,
work_comment:show,
work_comment_hidden:hidden,
data:data
})
}
} }
isdeleteModal=()=>{ isdeleteModal=(comment_id,visible_comment,type)=>{
let newcomment_id=comment_id;
let newvisible_comment=visible_comment;
let newtype=type;
this.setState({ this.setState({
modalsType: true, modalsType: true,
modalsTopval:"是否确认删除?", modalsTopval:"是否确认删除?",
modalSave: ()=>this.isdeleteModals(), modalSave: ()=>this.isdeleteModals(newcomment_id,newvisible_comment,newtype),
modalCancel:()=>this.hideisdeleteModal(), modalCancel:()=>this.hideisdeleteModals(),
}) })
} }
hideisdeleteModal=()=>{ hideisdeleteModals=()=>{
this.setState({
modalsType:false,
modalsTopval:"是否确认删除?",
modalSave: "",
modalCancel:"",
})
}
hideisdeleteModal=(comment_id,visible_comment,type)=>{
let{data,work_comment,work_comment_hidden}=this.state;
if(type==="child"){
data.stage_list.map((item,key)=>{
console.log(item)
if(item.comment_id!=null){
if(item.comment_id===comment_id){
item.challenge_comment=null;
item.challenge_comment_hidden=null;
}
}
})
this.setState({
modalsType:false,
modalsTopval:"是否确认删除?",
modalSave: "",
modalCancel:"",
data:data
})
}else{
this.setState({ this.setState({
modalsType: false, modalsType:false,
modalsTopval:"是否确认删除?", modalsTopval:"是否确认删除?",
modalSave: ()=>this.isdeleteModals(), modalSave: "",
modalCancel:()=>this.hideisdeleteModal(), modalCancel:"",
work_comment:null,
work_comment_hidden:null
}) })
}
} }
isdeleteModals=()=>{ isdeleteModals=(comment_id,visible_comment,type)=>{
let newcomment_id=comment_id;
let newvisible_comment=visible_comment;
let newtype=type;
if(comment_id!=null){
let url =`/student_works/${this.props.match.params.homeworkid}/destroy_work_comment.json` let url =`/student_works/${this.props.match.params.homeworkid}/destroy_work_comment.json`
axios.delete(url).then((response) => { axios.delete(url, { data: {
comment_id:comment_id,
}}).then((response) => {
// const { status } = response.data; // const { status } = response.data;
if(response.data.status===0){ if(response.data.status===0){
this.props.showNotification(response.data.message) this.props.showNotification(response.data.message)
this.setState({ this.hideisdeleteModal(newcomment_id,newvisible_comment,newtype)
showAppraiseModalsshow:false,
work_comment_hidden:false,
work_comment:undefined,
work_type:0,
})
this.hideisdeleteModal()
}else{ }else{
this.props.showNotification(response.data.message) this.props.showNotification(response.data.message)
} }
@ -233,8 +298,10 @@ class ShixunWorkReport extends Component {
console.log(error); console.log(error);
}); });
} }
}
render() { render() {
let{data,showAppraiseModaltype,work_comment_hidden,showAppraiseModalsshow,work_comment} =this.state; let{data,showAppraiseModaltype,work_comment_hidden,work_comment} =this.state;
let category_id=data===undefined?"":data.category===null?"":data.category.category_id; let category_id=data===undefined?"":data.category===null?"":data.category.category_id;
let homework_common_id=data===undefined?"":data.homework_common_id; let homework_common_id=data===undefined?"":data.homework_common_id;
@ -243,7 +310,7 @@ class ShixunWorkReport extends Component {
// let showAppraiseModals=this.props&&this.props.isAdminOrTeacher()===true?work_comment===null||work_comment===undefined?false:true:work_comment===null||work_comment===undefined?false:true; // let showAppraiseModals=this.props&&this.props.isAdminOrTeacher()===true?work_comment===null||work_comment===undefined?false:true:work_comment===null||work_comment===undefined?false:true;
let showAppraiseModals=work_comment===null||work_comment===undefined?false:true; let showAppraiseModals=work_comment===null||work_comment===undefined?false:true;
console.log(showAppraiseModals)
return ( return (
data===undefined?"":<Spin indicator={antIcon} spinning={this.state.spinning}> data===undefined?"":<Spin indicator={antIcon} spinning={this.state.spinning}>
@ -262,10 +329,12 @@ class ShixunWorkReport extends Component {
{...this.state} {...this.state}
visible={showAppraiseModaltype} visible={showAppraiseModaltype}
Cancel={()=>this.hideAppraiseModal()} Cancel={()=>this.hideAppraiseModal()}
showCancel={(list,type)=>this.showAppraiseModals(list,type)} showCancel={(show,hidden,id,comment_id)=>this.showAppraiseModals(show,hidden,id,comment_id)}
work_comment={this.state.work_comment} work_comment={this.state.work_comment}
work_type={work_comment===null||work_comment===undefined?this.state.work_type:work_comment_hidden===true?1:0} work_type={work_comment===null||work_comment===undefined?this.state.work_type:work_comment_hidden===true?1:0}
/>:""} />:""}
<div className="newMain clearfix "> <div className="newMain clearfix ">
<div className={"educontent mb20" }> <div className={"educontent mb20" }>
<div className="educontent"> <div className="educontent">
@ -303,9 +372,11 @@ class ShixunWorkReport extends Component {
{/*>评阅</a> : ""}*/} {/*>评阅</a> : ""}*/}
{this.props.isAdmin() ?<a {this.props.isAdmin() ?<a
className=" color-blue font-14 fr ml20 mt15" className=" color-blue font-14 fr ml20 mt15"
onClick={()=>this.showAppraiseModal("main")} onClick={()=>this.showAppraiseModal("main",undefined,work_comment,work_comment_hidden)}
>评阅</a>:""} >评阅</a>:""}
</div> </div>
{/*{work_comment===null||work_comment===undefined?"评阅":"编辑评阅"}*/} {/*{work_comment===null||work_comment===undefined?"评阅":"编辑评阅"}*/}
<style>{ <style>{
` `
@ -425,7 +496,7 @@ class ShixunWorkReport extends Component {
jumptopic={this.jumptopic} jumptopic={this.jumptopic}
getdatalist={()=>this.getdatalist()} getdatalist={()=>this.getdatalist()}
setupdalist={(challenge_score,overall_appraisal,work_score)=>this.setupdalist(challenge_score,overall_appraisal,work_score)} setupdalist={(challenge_score,overall_appraisal,work_score)=>this.setupdalist(challenge_score,overall_appraisal,work_score)}
showAppraiseModal={(type,id)=>this.showAppraiseModal(type,id)} showAppraiseModal={(type,id,show,hidden)=>this.showAppraiseModal(type,id,show,hidden)}
/> />
</div> </div>
@ -477,33 +548,12 @@ class ShixunWorkReport extends Component {
</div> </div>
{showAppraiseModals===true&&showAppraiseModalsshow===true?<div className="stud-class-set mt17"> <ShowAppraiseList
<div className="clearfix edu-back-white poll_list"> {...this.props}
{...this.state}
<div className="font-16 color-dark-21 shixunreporttitleboxtop pd20 color333"> isdeleteModal={(comment_id,visible_comment,type)=>this.isdeleteModal(comment_id,visible_comment,type)}
老师评阅<span>{work_comment_hidden===true||this.state.work_type===1?"(仅对课堂老师可见)":""}</span> showAppraiseModal={(type,id,show,hidden)=>this.showAppraiseModal(type,id,show,hidden)}
/>
{this.props&&this.props.isAdminOrTeacher()===true?<a className="color-blue font-14 fr ml20"
onClick={()=>this.isdeleteModal()}
>删除</a>:""}
{this.props&&this.props.isAdminOrTeacher()===true?<a className="color-blue font-14 fr"
onClick={()=>this.showAppraiseModal("main")}
>编辑</a>:""}
</div>
<div className="font-16 color-dark-21 shixunreporttitleboxbom pd30">
<div style={{minHeight:'50px'}}>
<div className={"personalsummary"}>
<div className={"markdown-body"}
dangerouslySetInnerHTML={{__html: markdownToHTML(work_comment).replace(/▁/g, "▁▁▁")}}></div>
</div>
</div>
</div>
</div>
</div>:""}
<div className="stud-class-set bor-bottom-greyE mt17"> <div className="stud-class-set bor-bottom-greyE mt17">

@ -0,0 +1,170 @@
import React,{ Component } from "react";
import { Modal,Checkbox,Upload,Button,Icon,message,Input,Radio} from "antd";
import { WordNumberTextarea,markdownToHTML } from 'educoder';
import axios from 'axios';
import './style.css';
class ShowAppraiseList extends Component{
constructor(props){
super(props);
this.state={
}
}
render(){
let {data, work_comment,work_comment_hidden}=this.props;
let work_commenttype=work_comment===undefined||work_comment===null||work_comment==="";
let work_comment_hiddentype=work_comment===undefined||work_comment===null||work_comment==="";
return(
<div>
<style>
{
`
.appraisebox{
width: 65px;
height: 22px;
background: rgba(76,172,255,1);
border-radius: 2px;
display: inline-block;
margin-right: 20px;
color: #fff;
font-size: 14px;
text-align: center;
line-height: 22px;
}
.markdown-body{
padding-bottom: 10px;
}
.borderbom{
margin-bottom: 15px;
border-bottom: 2px solid #fafafa;
}
`
}
</style>
{data===undefined?"":work_commenttype===true&&work_comment_hiddentype===true?"":
<div className="stud-class-set mt17">
<div className="clearfix edu-back-white poll_list">
<div className="font-16 color-dark-21 shixunreporttitleboxtop pd20 color333">
<span className={"appraisebox"}>总体评阅</span>
{this.props&&this.props.isAdmin()===true?<a className="color-blue font-14 fr ml20"
onClick={()=>this.props.isdeleteModal(data.comment_id,true,"main")}
>删除</a>:""}
{this.props&&this.props.isAdmin()===true?<a className="color-blue font-14 fr"
onClick={()=>this.props.showAppraiseModal("main",undefined,work_comment,work_comment_hidden)}
>编辑</a>:""}
</div>
{this.props&&this.props.isAdmin()===true?
<div className="font-16 color-dark-21 shixunreporttitleboxbom pd30bt">
<div>
<span className={"z000"}>学生可见<span className={"z666"}>学生可查看老师的评阅内容</span></span>
</div>
<div className={"borderbom"} style={{minHeight:'40px'}}>
{work_comment===true?"":<div className={"personalsummary"}>
<div className={"markdown-body"}
dangerouslySetInnerHTML={{__html: markdownToHTML(work_comment).replace(/▁/g, "▁▁▁")}}></div>
</div>}
</div>
<div>
<span className={"z000"}>学生不可见<span className={"z666"}>仅对课堂老师可见</span></span>
</div>
<div style={{minHeight:'40px'}}>
{work_comment_hidden===true?"":<div className={"personalsummary"}>
<div className={"markdown-body"}
dangerouslySetInnerHTML={{__html: markdownToHTML(work_comment_hidden).replace(/▁/g, "▁▁▁")}}></div>
</div>}
</div>
</div>:<div className="font-16 color-dark-21 shixunreporttitleboxbom pd30">
<div style={{minHeight:'40px'}}>
<div className={"personalsummary"}>
<div className={"markdown-body"}
dangerouslySetInnerHTML={{__html: markdownToHTML(work_comment).replace(/▁/g, "▁▁▁")}}></div>
</div>
</div>
</div>}
</div>
</div>}
{data===undefined?"":data.stage_list.map((item,key)=>{
let challenge_comment_hidden=item.challenge_comment_hidden===undefined||item.challenge_comment_hidden===null||item.challenge_comment_hidden==="";
let challenge_comment=item.challenge_comment===undefined||item.challenge_comment===null||item.challenge_comment==="";
return(
<div key={key}>
{challenge_comment===false&&challenge_comment_hidden==false?<div className="stud-class-set mt17" >
<div className="clearfix edu-back-white poll_list">
<div className="font-16 color-dark-21 shixunreporttitleboxtop pd20 color333">
<span className={"appraisebox"}>{key+1}</span>{item.name}
{this.props&&this.props.isAdmin()===true?<a className="color-blue font-14 fr ml20"
onClick={()=>this.props.isdeleteModal(item.comment_id,true,"child")}
>删除</a>:""}
{this.props&&this.props.isAdmin()===true?<a className="color-blue font-14 fr"
onClick={()=>this.props.showAppraiseModal("child",item.challenge_id,item.challenge_comment,item.challenge_comment_hidden)}
>编辑</a>:""}
</div>
{this.props&&this.props.isAdmin()===true?
<div className="font-16 color-dark-21 shixunreporttitleboxbom pd30bt">
<div>
<span className={"z000"}>学生可见<span className={"z666"}>学生可查看老师的评阅内容</span></span>
</div>
<div className={"borderbom"} style={{minHeight:'40px'}}>
{challenge_comment===true?"":<div className={"personalsummary"}>
<div className={"markdown-body"}
dangerouslySetInnerHTML={{__html: markdownToHTML(item.challenge_comment).replace(/▁/g, "▁▁▁")}}></div>
</div>}
</div>
<div>
<span className={"z000"}>学生不可见<span className={"z666"}>仅对课堂老师可见</span></span>
</div>
<div style={{minHeight:'40px'}}>
{challenge_comment_hidden===true?"":<div className={"personalsummary"}>
<div className={"markdown-body"}
dangerouslySetInnerHTML={{__html: markdownToHTML(item.challenge_comment_hidden).replace(/▁/g, "▁▁▁")}}></div>
</div>}
</div>
</div>: <div className="font-16 color-dark-21 shixunreporttitleboxbom pd30">
<div style={{minHeight:'40px'}}>
<div className={"personalsummary"}>
<div className={"markdown-body"}
dangerouslySetInnerHTML={{__html: markdownToHTML(item.challenge_comment).replace(/▁/g, "▁▁▁")}}></div>
</div>
</div>
</div>}
</div>
</div>:""}
</div>)
})
}
</div>
)
}
}
export default ShowAppraiseList;

@ -15,31 +15,7 @@ class OfficialAcademicTranscript extends Component {
} }
componentDidMount() { componentDidMount() {
let {data}=this.props;
let datas=[];
if(data!=undefined){
data.stage_list===undefined?"":data.stage_list.forEach((item,key)=>{
datas.push({
customs: key+1,
taskname:{name:item.name,complete_status:item.complete_status},
openingtime:item.open_time,
evaluating: item.evaluate_count,
finishtime:item.finished_time,
elapsedtime:item.time_consuming,
empvalue:{myself:item.myself_experience,experience:item.experience},
game_scores:{game_score:item.game_score,game_score_full:item.game_score_full},
challenge_id:{id:item.challenge_id},
challenge_comment: item.challenge_comment,
challenge_comment_hidden: item.challenge_comment_hidden,
// adjustmentminute:asdasd
})
})
this.setState({
datas:datas
})
}
} }
myjumptopic=(e)=>{ myjumptopic=(e)=>{
console.log("获取到值"); console.log("获取到值");
@ -98,7 +74,28 @@ class OfficialAcademicTranscript extends Component {
} }
render() { render() {
let {datas,customsids}=this.state; let {customsids}=this.state;
let {data}=this.props;
let datas=[];
if(data!=undefined){
data.stage_list===undefined?"":data.stage_list.forEach((item,key)=>{
datas.push({
customs: key+1,
taskname:{name:item.name,complete_status:item.complete_status},
openingtime:item.open_time,
evaluating: item.evaluate_count,
finishtime:item.finished_time,
elapsedtime:item.time_consuming,
empvalue:{myself:item.myself_experience,experience:item.experience},
game_scores:{game_score:item.game_score,game_score_full:item.game_score_full},
challenge_id:{id:item.challenge_id},
challenge_comment: item.challenge_comment,
challenge_comment_hidden: item.challenge_comment_hidden,
// adjustmentminute:asdasd
})
})
}
let columns=[{ let columns=[{
title: '关卡', title: '关卡',
@ -207,7 +204,7 @@ class OfficialAcademicTranscript extends Component {
render: (text, record) => ( render: (text, record) => (
<span> <span>
{console.log(record)}
<a <a
className=" color-blue font-14 fr mr22" className=" color-blue font-14 fr mr22"
onClick={()=>this.props.showAppraiseModal("child",record.challenge_id.id,record.challenge_comment,record.challenge_comment_hidden)} onClick={()=>this.props.showAppraiseModal("child",record.challenge_id.id,record.challenge_comment,record.challenge_comment_hidden)}

@ -75,3 +75,16 @@
.ysyslxh{ .ysyslxh{
background: #fafafa; background: #fafafa;
} }
.z666{
color: #666;
font-size:14px;
}
.z000{
color: #000;
font-size:16px;
}
.pd30bt{
padding: 10px 30px 0px 30px;
}

@ -144,6 +144,11 @@ class VNCContainer extends Component {
.codeEvaluateDrawer .ant-drawer-body { .codeEvaluateDrawer .ant-drawer-body {
padding: 0px; padding: 0px;
} }
.codeEvaluateDrawer .ant-drawer-content-wrapper, .codeEvaluateDrawer .ant-drawer-mask {
position: absolute;
}
.codeEvaluateFloatButton { .codeEvaluateFloatButton {
bottom: 180px !important; bottom: 180px !important;
left: unset; left: unset;
@ -211,7 +216,7 @@ class VNCContainer extends Component {
mask={true} mask={true}
title="" title=""
width={firstDrawerWidth} width={firstDrawerWidth}
// closable={false} closable={false}
onClose={this.onBottomDrawerClose} onClose={this.onBottomDrawerClose}
visible={this.state.bottomDrawer} visible={this.state.bottomDrawer}
className={'codeEvaluateDrawer'} className={'codeEvaluateDrawer'}

@ -459,8 +459,8 @@ class PathDetailIndex extends Component{
this.props.checkIfLogin()===false?"":progress === undefined ? "" : progress === null ? "" : this.props.checkIfLogin()===false?"":progress === undefined ? "" : progress === null ? "" :
<div className="edu-back-white myProgress padding40-20 mb10"> <div className="edu-back-white myProgress padding40-20 mb10">
<p className="mb20"> <p className="mb20">
<span className="font-16 mr10">关卡数</span> <span className="font-16 mr10">我的进展</span>
<Tooltip placement="bottom" title="已通过关卡数/总关卡数"> <Tooltip placement="bottom" title="获得经验值/总经验值">
<span className="color-green" >{progress.my_score} / {progress.all_score}</span> <span className="color-green" >{progress.my_score} / {progress.all_score}</span>
</Tooltip> </Tooltip>
</p> </p>

@ -190,6 +190,8 @@ class TPMIndex extends Component {
identity: response.data.identity, identity: response.data.identity,
propaedeutics:response.data.propaedeutics, propaedeutics:response.data.propaedeutics,
status: response.data.shixun_status, status: response.data.shixun_status,
secret_repository: response.data.secret_repository,
}); });
} }
}).catch((error) => { }).catch((error) => {
@ -285,6 +287,10 @@ class TPMIndex extends Component {
(props) => (<TPMRepositoryCommits {...this.props} {...this.state} {...props} (props) => (<TPMRepositoryCommits {...this.props} {...this.state} {...props}
/>) />)
}></Route> }></Route>
<Route path="/shixuns/:shixunId/secret_repository/:repoId/commits" render={
(props) => (<TPMRepositoryCommits {...this.props} {...this.state} {...props} secret_repository_tab={true}
/>)
}></Route>
<Route exact path="/shixuns/:shixunId/challenges" render={ <Route exact path="/shixuns/:shixunId/challenges" render={
(props) => (<TPMChallengeComponent {...this.props} {...this.state} {...props} (props) => (<TPMChallengeComponent {...this.props} {...this.state} {...props}
@ -300,6 +306,11 @@ class TPMIndex extends Component {
(props) => (<TPMRepositoryComponent {...this.props} {...this.state} {...props} (props) => (<TPMRepositoryComponent {...this.props} {...this.state} {...props}
/>) />)
}></Route> }></Route>
<Route path="/shixuns/:shixunId/secret_repository" render={
(props) => (<TPMRepositoryComponent {...this.props} {...this.state} {...props} secret_repository_tab={true}
/>)
}></Route>
{/* <Route exact path="/shixuns/:shixunId/propaedeutics" component={TPMPropaedeuticsComponent}></Route> */} {/* <Route exact path="/shixuns/:shixunId/propaedeutics" component={TPMPropaedeuticsComponent}></Route> */}
<Route exact path="/shixuns/:shixunId/propaedeutics" render={ <Route exact path="/shixuns/:shixunId/propaedeutics" render={

@ -11,6 +11,8 @@ import Repository from './shixunchild/Repository/Repository'
import TPMRightSection from './component/TPMRightSection' import TPMRightSection from './component/TPMRightSection'
import TPMNav from './component/TPMNav' import TPMNav from './component/TPMNav'
// import RepositoryChooseModal from './component/modal/RepositoryChooseModal'
class TPMRepository extends Component { class TPMRepository extends Component {
constructor(props) { constructor(props) {
super(props) super(props)
@ -34,6 +36,7 @@ class TPMRepository extends Component {
shixun={shixun} shixun={shixun}
{...this.props} {...this.props}
></TPMNav> ></TPMNav>
{/* <RepositoryChooseModal {...this.props}></RepositoryChooseModal> */}
{ loadingContent ? { loadingContent ?
<CircularProgress size={40} thickness={3} style={{ marginLeft: 'auto', marginRight: 'auto', marginTop: '200px', display: 'block' }}/> : <CircularProgress size={40} thickness={3} style={{ marginLeft: 'auto', marginRight: 'auto', marginTop: '200px', display: 'block' }}/> :
<Repository <Repository

@ -30,6 +30,12 @@ class TPMRepositoryComponent extends Component {
isContentWidth100: this._isFileInPathArray(pathArray) isContentWidth100: this._isFileInPathArray(pathArray)
} }
} }
componentDidUpdate(prevProps, prevState) {
if (this.props.secret_repository_tab != prevProps.secret_repository_tab) {
this.fetchRepo()
}
}
componentDidMount = () => { componentDidMount = () => {
this.fetchRepo() this.fetchRepo()
@ -72,7 +78,8 @@ class TPMRepositoryComponent extends Component {
let id = this.props.match.params.shixunId; let id = this.props.match.params.shixunId;
let url = `/shixuns/${id}/file_content.json`; let url = `/shixuns/${id}/file_content.json`;
axios.post(url, { axios.post(url, {
path: path path: path,
secret_repository: this.props.secret_repository_tab
}).then((response) => { }).then((response) => {
trace_collapse('repository res: ', response) trace_collapse('repository res: ', response)
@ -138,7 +145,7 @@ class TPMRepositoryComponent extends Component {
const path = urlNewPathArray.join('/') const path = urlNewPathArray.join('/')
let id = this.props.match.params.shixunId; let id = this.props.match.params.shixunId;
let url = `/shixuns/${id}/repository.json`; let url = `/shixuns/${id}/${this.props.secret_repository_tab ? 'secret_repository' : 'repository'}.json`;
// this.props.setLoadingContent(true) // this.props.setLoadingContent(true)
axios.post(url, { axios.post(url, {
path: path ? path : '' path: path ? path : ''

@ -353,6 +353,8 @@ export default class TPMsettings extends Component {
test_set_permission: response.data.shixun.test_set_permission, test_set_permission: response.data.shixun.test_set_permission,
hide_code: response.data.shixun.hide_code, hide_code: response.data.shixun.hide_code,
code_hidden: response.data.shixun.code_hidden, code_hidden: response.data.shixun.code_hidden,
is_secret_repository: response.data.shixun.is_secret_repository,
init_is_secret_repository: response.data.shixun.is_secret_repository,
forbid_copy: response.data.shixun.forbid_copy, forbid_copy: response.data.shixun.forbid_copy,
vnc: response.data.shixun.vnc, vnc: response.data.shixun.vnc,
vnc_evaluate: response.data.shixun.vnc_evaluate, vnc_evaluate: response.data.shixun.vnc_evaluate,
@ -439,22 +441,54 @@ export default class TPMsettings extends Component {
SelectshixunCommand=(e)=>{ SelectshixunCommand=(e)=>{
// console.log( e.target.value) // console.log( e.target.value)
const webssh = e.target.value
if (webssh == 2) {
this.setState({ this.setState({
webssh: e.target.value, webssh: webssh,
SelectTheCommandtype: true,
multi_webssh:false
}); });
if(e.target.value===2){ } else {
if (this.state.init_is_secret_repository && !this.state.vnc && this.state.is_secret_repository == true) {
this.confirmDeleteSecretRepo({
onOk: () => {
this.setState({ this.setState({
SelectTheCommandtype: true, webssh: webssh,
SelectTheCommandtype: false,
multi_webssh:false multi_webssh:false
}); });
}else{ }
})
} else {
if (!this.state.vnc) {
this.setState({ this.setState({
is_secret_repository: false,
})
}
this.setState({
webssh: webssh,
SelectTheCommandtype: false, SelectTheCommandtype: false,
multi_webssh:false multi_webssh:false
}); });
} }
} }
// this.setState({
// webssh: webssh,
// });
// if(webssh===2){
// this.setState({
// SelectTheCommandtype: true,
// multi_webssh:false
// });
// }else{
// this.setState({
// SelectTheCommandtype: false,
// multi_webssh:false
// });
// }
}
SelectOpenpublic=(e)=>{ SelectOpenpublic=(e)=>{
this.setState({ this.setState({
Openpublic: e.target.value Openpublic: e.target.value
@ -525,6 +559,35 @@ export default class TPMsettings extends Component {
}); });
} }
confirmDeleteSecretRepo = ({title, onOk}) => {
confirm({
title: title || <div>
<div>已创建的私密版本库及其内容将在保存时被删除</div>
<div>是否确认取消勾选</div>
</div>,
okText: '确定',
cancelText: '取消',
onOk: () => {
this.setState({ is_secret_repository: false })
onOk && onOk()
},
onCancel() {
},
});
}
is_secret_repository = (e) => {
const checked = e.target.checked
if (!checked) {
if (this.state.init_is_secret_repository) {
this.confirmDeleteSecretRepo({
})
} else {
this.setState({ is_secret_repository: false })
}
} else {
this.setState({ is_secret_repository: true })
}
}
forbid_copy = (e) => { forbid_copy = (e) => {
let sum = "" let sum = ""
if (e.target.checked === false) { if (e.target.checked === false) {
@ -550,11 +613,34 @@ export default class TPMsettings extends Component {
// } else if (e.target.checked === true) { // } else if (e.target.checked === true) {
// sum = 1 // sum = 1
// } // }
const vnc = e.target.checked;
if (!vnc) {
if (this.state.init_is_secret_repository && this.state.webssh != 2 && this.state.is_secret_repository == true) {
this.confirmDeleteSecretRepo({
onOk: () => {
this.setState({
vnc: e.target.checked,
vnc_evaluate: false,
});
}
})
} else {
if (this.state.webssh != 2) {
this.setState({
is_secret_repository: false
})
}
this.setState({
vnc: e.target.checked,
vnc_evaluate: false,
});
}
} else {
this.setState({ this.setState({
vnc: e.target.checked, vnc: e.target.checked,
vnc_evaluate: false, vnc_evaluate: false,
}); });
}
} }
shixunsname = (e) => { shixunsname = (e) => {
// let {shixunsstatus}=this.state; // let {shixunsstatus}=this.state;
@ -773,7 +859,8 @@ export default class TPMsettings extends Component {
// }); // });
// } // }
submit_edit_shixun = () => { submit_edit_shixun = () => {
if (this.saving == true) return;
this.saving = true;
if(this.state.status===-1){ if(this.state.status===-1){
this.props.showSnackbar("该实训已被删除,保存失败!"); this.props.showSnackbar("该实训已被删除,保存失败!");
return return
@ -782,7 +869,7 @@ export default class TPMsettings extends Component {
let { let {
name, choice_main_type, choice_small_type, choice_standard_scripts, scope_partment, choice_standard_scriptssum, vnc_evaluate, name, choice_main_type, choice_small_type, choice_standard_scripts, scope_partment, choice_standard_scriptssum, vnc_evaluate,
evaluate_script, webssh, use_scope, trainee, can_copy, task_pass, test_set_permission, hide_code, code_hidden, forbid_copy, vnc,multi_webssh, evaluate_script, webssh, use_scope, trainee, can_copy, task_pass, test_set_permission, hide_code, code_hidden, forbid_copy, vnc,multi_webssh,
opening_time,shixunmemoMDvalue,shixun_service_configlist opening_time,shixunmemoMDvalue,shixun_service_configlist, is_secret_repository
} = this.state; } = this.state;
let newshixun_service_configlist = shixun_service_configlist.map(v => { let newshixun_service_configlist = shixun_service_configlist.map(v => {
@ -886,6 +973,7 @@ export default class TPMsettings extends Component {
let Url = `/shixuns/` + id + `.json`; let Url = `/shixuns/` + id + `.json`;
let data = { let data = {
shixun:{ shixun:{
name: name, name: name,
webssh: webssh, webssh: webssh,
use_scope: use_scope, use_scope: use_scope,
@ -906,6 +994,7 @@ export default class TPMsettings extends Component {
description: description_editormd, description: description_editormd,
evaluate_script: evaluate_script_editormd, evaluate_script: evaluate_script_editormd,
}, },
is_secret_repository: is_secret_repository,
main_type: choice_main_type, main_type: choice_main_type,
small_type: choice_small_type, small_type: choice_small_type,
scope_partment: scope_partment, scope_partment: scope_partment,
@ -914,6 +1003,7 @@ export default class TPMsettings extends Component {
axios.put(Url, data).then((response) => { axios.put(Url, data).then((response) => {
// console.log(response) // console.log(response)
this.saving = false;
if(response.status){ if(response.status){
if (response.data.status === -1) { if (response.data.status === -1) {
this.props.showSnackbar(response.data.message); this.props.showSnackbar(response.data.message);
@ -925,6 +1015,7 @@ export default class TPMsettings extends Component {
}).catch((error) => { }).catch((error) => {
console.log(error) console.log(error)
this.saving = false;
}) })
@ -1460,6 +1551,7 @@ export default class TPMsettings extends Component {
name, name,
settingsData, settingsData,
webssh, webssh,
is_secret_repository,
use_scope, use_scope,
shixunsID, shixunsID,
can_copy, can_copy,
@ -2199,6 +2291,15 @@ export default class TPMsettings extends Component {
</span> </span>
</div> </div>
{ (vnc || webssh == 2) && <div className="clearfix mt20 ml30">
<span className="color-grey-6 mt5 fl" style={{minWidth: '95px'}}>私密版本库:</span>
<span className="fl mt5">
<Checkbox checked={is_secret_repository === undefined ? false : is_secret_repository}
onChange={this.is_secret_repository}></Checkbox>
<label style={{top:'6px'}} className="color-grey-9 ml10" >勾选则启用私密版本库学员页面不能查看该版本库目录</label>
</span>
</div>}
<div className="clearfix mt20 ml30"> <div className="clearfix mt20 ml30">
<span className="color-grey-6 mt5 fl" style={{minWidth: '95px'}}>禁用复制粘贴:</span> <span className="color-grey-6 mt5 fl" style={{minWidth: '95px'}}>禁用复制粘贴:</span>
<span className="fl mt5"> <span className="fl mt5">
@ -2239,7 +2340,7 @@ export default class TPMsettings extends Component {
<span className="color-grey-6 mt5 fl" style={{minWidth: '95px'}}>VNC图形化评测:</span> <span className="color-grey-6 mt5 fl" style={{minWidth: '95px'}}>VNC图形化评测:</span>
<span className="fl mt5 ml5"> <span className="fl mt5 ml5">
<Checkbox checked={vnc_evaluate === undefined ? false : vnc_evaluate} onChange={this.shixun_vnc_evaluate}></Checkbox> <Checkbox checked={vnc_evaluate === undefined ? false : vnc_evaluate} onChange={this.shixun_vnc_evaluate}></Checkbox>
<label style={{top:'6px'}} className="color-grey-9 ml10" >勾选则在VNC图形化实训中给学员开启评测</label> <label style={{top:'6px'}} className="color-grey-9 ml10" >勾选则在学员的VNC图形化页面中开启评测功能</label>
</span> </span>
</div>:""} </div>:""}

@ -240,6 +240,7 @@ export default class TPMevaluation extends Component {
this.setState({ this.setState({
evaluationlist:newevaluationlist evaluationlist:newevaluationlist
}) })
console.log(newevaluationlist)
} }
@ -539,6 +540,14 @@ export default class TPMevaluation extends Component {
this.setevaluationlist(newevaluationlist); this.setevaluationlist(newevaluationlist);
} }
// 修改测试集的匹配规则
changeEvaluationRule=(e,key)=>{
let {evaluationlist}=this.state;
let newevaluationlist=evaluationlist;
newevaluationlist[key].match_rule=e.target.value
this.setevaluationlist(newevaluationlist);
}
evaluationoninputvalue=(e,key,type)=>{ evaluationoninputvalue=(e,key,type)=>{
$.fn.autoHeight = function(){ $.fn.autoHeight = function(){
function autoHeight(elem){ function autoHeight(elem){
@ -1159,6 +1168,13 @@ export default class TPMevaluation extends Component {
autoHeight="true" autoHeight="true"
onInput={(e)=>this.evaluationoninputvalue(e,key,"yq")} onInput={(e)=>this.evaluationoninputvalue(e,key,"yq")}
></textarea> ></textarea>
<div className="clearfix lineh-30">
<span className="fl mr10 color-grey-6">匹配规则</span>
<RadioGroup className="fl" value={item.match_rule} onChange={(e)=>this.changeEvaluationRule(e,key)}>
<Radio value='full'>完全匹配</Radio>
<Radio value='last'>末尾匹配</Radio>
</RadioGroup>
</div>
</div> </div>
) )
})} })}

@ -5,7 +5,7 @@ import { BrowserRouter as Router, Route, Link } from "react-router-dom";
class TPMNav extends Component { class TPMNav extends Component {
render() { render() {
const { user, match, shixun } = this.props; const { user, match, shixun, secret_repository } = this.props;
let isAdminOrCreator = false; let isAdminOrCreator = false;
if (user) { if (user) {
isAdminOrCreator = user.admin || user.manager isAdminOrCreator = user.admin || user.manager
@ -30,7 +30,10 @@ class TPMNav extends Component {
<Link to={`/shixuns/${shixunId}/repository`} <Link to={`/shixuns/${shixunId}/repository`}
style={{display: this.props.identity >4||this.props.identity===undefined ? "none" : 'block'}} style={{display: this.props.identity >4||this.props.identity===undefined ? "none" : 'block'}}
className={`${match.url.indexOf('repository') != -1 ? 'active' : ''} fl mr40`}>版本库</Link> className={`${match.url.indexOf('/repository') != -1 ? 'active' : ''} fl mr40`}>版本库</Link>
{secret_repository && <Link to={`/shixuns/${shixunId}/secret_repository`}
style={{display: this.props.identity >4||this.props.identity===undefined ? "none" : 'block'}}
className={`${match.url.indexOf('secret_repository') != -1 ? 'active' : ''} fl mr40`}>私密版本库</Link>}
<Link to={`/shixuns/${shixunId}/collaborators`} <Link to={`/shixuns/${shixunId}/collaborators`}
className={`${match.url.indexOf('collaborators') != -1 ? 'active' : ''} fl mr40`}>合作者</Link> className={`${match.url.indexOf('collaborators') != -1 ? 'active' : ''} fl mr40`}>合作者</Link>

@ -121,7 +121,7 @@ class TPMRightSection extends Component {
<div className="padding20 edu-back-white mb10 mt10" style={{ <div className="padding20 edu-back-white mb10 mt10" style={{
display: TPMRightSectionData === undefined?"none":TPMRightSectionData.paths===undefined?"":TPMRightSectionData.paths.length === 0 ? "none" : "block" display: TPMRightSectionData === undefined?"none":TPMRightSectionData.paths===undefined?"":TPMRightSectionData.paths.length === 0 ? "none" : "block"
}}> }}>
<p className="mb20 font-16 clearfix">所属课程</p> <p className="mb20 font-16 clearfix">相关实践课程</p>
<div className="recommend-list" > <div className="recommend-list" >
{ {
TPMRightSectionData===undefined?"":TPMRightSectionData.paths===undefined?"":TPMRightSectionData.paths.map((i,k)=>{ TPMRightSectionData===undefined?"":TPMRightSectionData.paths===undefined?"":TPMRightSectionData.paths.map((i,k)=>{
@ -164,7 +164,7 @@ class TPMRightSection extends Component {
<div className="padding20 edu-back-white" <div className="padding20 edu-back-white"
style={{ style={{
display: display:
TPMRightSectionData === undefined?"none":TPMRightSectionData.recommands===undefined?"none":TPMRightSectionData.recommands.length === 0 ||TPMRightSectionData.paths===undefined?"":TPMRightSectionData.paths.length === 0 ? "none" : "block" TPMRightSectionData === undefined?"none":TPMRightSectionData.recommands===undefined?"none":TPMRightSectionData.recommands.length === 0 ? "none" : "block"
}} }}
> >
<p className="mb20 font-16 clearfix">推荐实训</p> <p className="mb20 font-16 clearfix">推荐实训</p>

@ -0,0 +1,153 @@
// import React, { useState, useEffect, memo } from 'react';
// import axios from 'axios'
// import { Modal, Input } from 'antd';
// function RepositoryChooseModal(props) {
// const [trees, setTrees] = useState([])
// const [path, setPath] = useState('')
// const [pathArray, setPathArray] = useState([{val: "根目录/", path: ""}])
// const [modalVisible, setModalVisible] = useState(true)
// useEffect(() => {
// repository('')
// }, [])
// function onOk() {
// }
// function onCancel() {
// }
// /**
// 点nav 会传入key
// 点item 会传入 newPath
// item => name, type type tree/leaf
// */
// const repository=(item, key, newPath)=>{
// let newPathArray = [] //
// //
// if (key) {
// for(var i=0; i<=key; i++){
// newPathArray.push(pathArray[i])
// }
// } else if (item) {
// newPathArray = pathArray.slice(0)
// newPathArray.push({val: item.name, path: pathArray[pathArray.length - 1] + "/" + item.name})
// }
// const path = item || key ? newPathArray[newPathArray.length - 1] : ''
// let id = props.match.params.shixunId;
// let url ="/shixuns/"+id+"/repository.json";
// axios.post(url,{
// path: path
// }).then((response) => {
// if (response.data.status === 403||response.data.status === 401||response.data.status === 500) {
// }else{
// setTrees(response.data.trees)
// setPath(path)
// pathArray(newPathArray)
// }
// }).catch((error) => {
// console.log(error)
// });
// }
// const savegetfilepath=(value)=>{
// const state = {}
// let {selectpath,saveshixunfilepath,pathtype} = state
// if(pathtype===1){
// let newselectpath;
// if(saveshixunfilepath==="shixunfilepathplay"){
// newselectpath=value
// }else{
// const type = selectpath.split('');
// let types=false;
// for(var i=0; i<type.length; i++){
// if(type[i]===value){
// types=true
// return
// }
// }
// if(types===false){
// newselectpath=selectpath+value+ ""
// }else{
// newselectpath=selectpath
// }
// }
// // this.setState({
// // // selectpatharr:newarr,
// // selectpath: newselectpath,
// // })
// }
// }
// const goblakepath=(path,key)=>{
// }
// function sendgetfilepath() {
// }
// return (
// <Modal
// keyboard={false}
// title="文件路径"
// visible={modalVisible}
// closable={false}
// footer={false}
// >
// <div className="task_popup_con">
// <div className="newupload_conbox clearfix">
// <ul id="directory_file">
// {/*文件导航*/}
// {
// pathArray.length===0?"":pathArray.map((item,key)=>{
// return(
// <a className="f14 fb" onClick={()=>goblakepath(item.path,key,item)}>{item.val}</a>
// )
// })
// }
// {/*文件*/}
// {trees === undefined || trees === null ? "" : trees.map((item, key) => {
// return(
// <li className="entry" key={key}>
// <div className="filename_no_report hidden">{
// item.type==="tree"?<a onClick={()=>sendgetfilepath(item.name,item.type,path+item.name)} data-remote="true">
// <i className="iconfont icon-wenjianjia color-blue mr2"></i>
// {path+item.name}</a>:<a data-remote="true">
// <i className="iconfont icon-zuoye color-blue mr2"></i>
// <span onClick={()=>savegetfilepath(path+item.name,item.type)}>{path+item.name}</span>
// </a>
// }
// </div>
// </li>
// )
// })}
// </ul>
// <div className="clearfix mt20">
// <label className="fl mt5 directory_filepath">选中的文件路径:</label>
// <Input id="points_tusi" placeholder="选中的文件路径" className="fl input-60-40"
// style={{width:"400px"}}
// onInput={(e)=>saveselectpath(e)}
// value={path}/>
// </div>
// <a className="task-btn task-btn-orange fr"
// style={{marginTop: '20px',marginLeft:'20px'}} id="add_path" onClick={()=>onOk()}>确定</a>
// <a className="pop_close task-btn mb10 fr"
// style={{marginTop: '20px'}} id="back_page" onClick={()=>onCancel()}>取消</a>
// </div>
// </div>
// </Modal>
// )
// }
// export default RepositoryChooseModal

@ -15,7 +15,7 @@ import { trace, trace_collapse ,getImageUrl, toPath} from "educoder";
import RepositoryDirectories from './RepositoryDirectories' import RepositoryDirectories from './RepositoryDirectories'
import { ActionBtn , NoneData } from 'educoder' import { ActionBtn , NoneData } from 'educoder'
import RepositoryCombinePath from './RepositoryCombinePath'
const $ = window.$; const $ = window.$;
// 点击按钮复制功能 // 点击按钮复制功能
@ -85,10 +85,13 @@ class Repository extends Component {
className=" guideBtn" >Git使用指南</a> className=" guideBtn" >Git使用指南</a>
{ {
this.props.current_user && (this.props.current_user.admin ==true || (TPMRightSectionData && TPMRightSectionData.creator && TPMRightSectionData.creator.login == this.props.current_user.login)) ? this.props.current_user && (this.props.current_user.admin ==true || (TPMRightSectionData && TPMRightSectionData.creator && TPMRightSectionData.creator.login == this.props.current_user.login)) ?
<ActionBtn style="orangeLine" className="ml20" to={`/shixuns/${match.params.shixunId}/repository/add_file`}>+添加文件</ActionBtn>:"" !this.props.secret_repository_tab &&
<ActionBtn style="orangeLine" className="ml20" to={`/shixuns/${match.params.shixunId}/repository/add_file`}>+添加文件</ActionBtn>
:""
} }
<div className="fr font-12 color-grey-9 pr"> <div className="fr font-12 color-grey-9 pr">
<label className="fl mt2">网址克隆</label> <label className="fl mt2">网址克隆</label>
<input type="text" id="copy_rep_content" className="fl url-input mt2" <input type="text" id="copy_rep_content" className="fl url-input mt2"
@ -155,6 +158,10 @@ class Repository extends Component {
</div> </div>
</div> </div>
{this.props.secret_repository_tab && <RepositoryCombinePath {...this.props}>
</RepositoryCombinePath>}
</div> </div>
</div> </div>
@ -181,7 +188,7 @@ class Repository extends Component {
{commits===undefined?"":commits[0].time} {commits===undefined?"":commits[0].time}
</acronym> {commits===undefined?"":commits[0].title} </acronym> {commits===undefined?"":commits[0].title}
</span> </span>
<a href={`/shixuns/${match.params.shixunId}/repository/${match.params.shixunId}/commits`} <a href={`/shixuns/${match.params.shixunId}/${this.props.secret_repository_tab ? 'secret_repository' : 'repository'}/${match.params.shixunId}/commits`}
className="color-grey-6 fr font-16 "> className="color-grey-6 fr font-16 ">
<i className="iconfont icon-tijiaojilu font-18 fl mr5"></i> <i className="iconfont icon-tijiaojilu font-18 fl mr5"></i>
<span className="fl mt2">提交记录</span> <span className="fl mt2">提交记录</span>

@ -121,6 +121,7 @@ class RepositoryCodeEditor extends Component {
const path = pathArray.join('/') const path = pathArray.join('/')
this.setState({ codeSaving: true }) this.setState({ codeSaving: true })
axios.post(url, { axios.post(url, {
secret_repository: this.props.secret_repository_tab,
content: this.extend_editor.getValue(), content: this.extend_editor.getValue(),
// type: forTest === true ? 1 : 0, // type: forTest === true ? 1 : 0,
path: path path: path

@ -0,0 +1,82 @@
import React, { Component } from 'react';
import { Redirect } from 'react-router';
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import PropTypes from 'prop-types';
import classNames from 'classnames';
import axios from 'axios';
import { trace_collapse, WordsBtn } from 'educoder'
import { message, Input } from 'antd';
const $ = window.$;
class RepositoryCombinePath extends Component {
constructor(props) {
super(props)
this.state = {
value: this.props.secret_dir_path || '',
isEdit: false,
}
}
onSave = () => {
const { shixunId, pathArray } = this.props;
const url = `/shixuns/${shixunId}/set_secret_dir.json`
this.setState({ codeSaving: true })
axios.post(url, {
secret_dir_path: this.state.value
}
).then((response) => {
if (response.data) {
message.success('保存成功');
this.setState({isEdit: false})
}
})
}
onChange = (e) => {
const { value } = e.target;
this.setState({ value })
}
onEdit = () => {
this.setState({isEdit: true}, () => {
window.$('.combinePathEditRow input')[0].focus()
});
}
render() {
const { fileContent, match, saveCode } = this.props;
const { isEdit, value } = this.state;
return (
<div className="df combinePathEditRow">
<style>{`
.combinePathEditRow {
margin: 4px 0;
}
.combinePathEditRow input {
flex: 0 0 300px;
border: none;
}
.combinePathEditRow .wordsBtn {
margin-left: 24px;
}
`}</style>
<span>第一版本库合并路径</span>
<Input disabled={!isEdit} value={value} onChange={this.onChange}></Input>
{!isEdit && <WordsBtn className="wordsBtn" onClick={this.onEdit} style="blue">修改</WordsBtn>}
{isEdit && <WordsBtn className="wordsBtn" onClick={this.onSave} style="blue">保存</WordsBtn>}
</div>
);
}
}
export default RepositoryCombinePath;

@ -34,7 +34,9 @@ class TPMRepositoryCommits extends Component {
let id = this.props.match.params.shixunId; let id = this.props.match.params.shixunId;
let collaborators=`/shixuns/`+id+`/commits.json`; let collaborators=`/shixuns/`+id+`/commits.json`;
axios.post(collaborators).then((response)=> { axios.post(collaborators, {
secret_repository: this.props.secret_repository_tab
}).then((response)=> {
if(response.status===200){ if(response.status===200){
this.setState({ this.setState({

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