diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb index b08ba2dbd..c360972c3 100644 --- a/app/controllers/courses_controller.rb +++ b/app/controllers/courses_controller.rb @@ -1318,7 +1318,7 @@ class CoursesController < ApplicationController @c_tasks = @course.graduation_tasks.task_published.order("graduation_tasks.publish_time asc, graduation_tasks.created_at asc") set_export_cookies - member_to_xlsx(@course, @all_members, @c_homeworks, @c_exercises, @c_tasks) + member_to_xlsx(@course, @all_members.includes(user: :user_extension), @c_homeworks, @c_exercises, @c_tasks) filename_ = "#{current_user.real_name}_#{@course.name}_总成绩_#{Time.now.strftime('%Y%m%d_%H%M%S')}" render xlsx: "#{format_sheet_name filename_.strip}",template: "courses/export_member_scores_excel.xlsx.axlsx", locals: {course_scores:@course_user_scores,shixun_works:@shixun_work_arrays, diff --git a/app/controllers/games_controller.rb b/app/controllers/games_controller.rb index 1816b032d..d55eb9211 100644 --- a/app/controllers/games_controller.rb +++ b/app/controllers/games_controller.rb @@ -3,7 +3,7 @@ class GamesController < ApplicationController before_action :find_game, except: [:jupyter] before_action :find_shixun, only: [:show, :answer, :rep_content, :choose_build, :game_build, :game_status] - before_action :allowed + before_action :allowed, except: [:jupyter] #require 'iconv' @@ -95,6 +95,9 @@ class GamesController < ApplicationController def jupyter # Jupyter没有challenge @myshixun = Myshixun.find_by_identifier params[:identifier] + unless current_user.id == @myshixun.user_id || current_user.admin_or_business? + raise Educoder::TipException.new(403, "..") + end @shixun = @myshixun.shixun # 判断tpm是否修改了 begin diff --git a/app/controllers/jupyters_controller.rb b/app/controllers/jupyters_controller.rb index 1eefd3d53..ab3962cbc 100644 --- a/app/controllers/jupyters_controller.rb +++ b/app/controllers/jupyters_controller.rb @@ -1,4 +1,3 @@ -require 'net/http' class JupytersController < ApplicationController include JupyterService @@ -6,8 +5,8 @@ class JupytersController < ApplicationController before_action :shixun, only: [:open, :open1, :test, :save] def save_with_tpi - game = Game.find_by(identifier: params[:identifier]) - jupyter_save_with_game(game, params[:jupyter_port]) + myshixun = Myshixun.find_by(identifier: params[:identifier]) + jupyter_save_with_game(myshixun, params[:jupyter_port]) render json: {status: 0} end @@ -18,9 +17,9 @@ class JupytersController < ApplicationController end def get_info_with_tpi - game = Game.find_by(identifier: params[:identifier]) - url = jupyter_url_with_game(game) - port = jupyter_port_with_game(game) + myshixun = Myshixun.find_by(identifier: params[:identifier]) + url = jupyter_url_with_game(myshixun) + port = jupyter_port_with_game(myshixun) render json: {status: 0, url: url, port: port} end @@ -32,89 +31,4 @@ class JupytersController < ApplicationController end - private - - def open - #打开tpm - juypter接口 - shixun = @shixun - shixun_tomcat = edu_setting('cloud_bridge') - uri = "#{shixun_tomcat}/bridge/jupyter/get" - tpiID = "tpm#{shixun.id}" - params = {tpiID: tpiID, :containers => "#{Base64.urlsafe_encode64(shixun_container_limit(shixun))}"} - - - logger.info "test_juypter: uri->#{uri}, params->#{params}" - - res = uri_post uri, params - - logger.info "test_juypter: #{res}" - - render plain: "https://#{res['port']}.jupyter.educoder.net/notebooks/data/workspace/myshixun_#{tpiID}/01.ipynb" - end - - def open1 - ## 打开tpi - game = Game.find(2170158) - shixun = game.myshixun.shixun - shixun_tomcat = edu_setting('cloud_bridge') - uri = "#{shixun_tomcat}/bridge/jupyter/get" - - tpiID = game.myshixun.id - params = {tpiID: tpiID, :containers => "#{Base64.urlsafe_encode64(shixun_container_limit(shixun))}"} - res = uri_post uri, params - - logger.info "test_juypter: #{res}" - - if res && res['code'].to_i != 0 - raise("实训云平台繁忙(繁忙等级:99)") - end - - repo_save_path = game.myshixun.repo_save_path - - render plain: "https://#{res['port']}.jupyter.educoder.net/notebooks/data/workspace/myshixun_#{tpiID}/#{repo_save_path}/01.ipynb" - end - - - def test - render plain: 'test' - end - - def save() - # 保存01.ipy - - author_name = current_user.real_name - author_email = current_user.git_mail - message = "User submitted" - - - #https://47526.jupyter.educoder.net/nbconvert/notebook/data/workspace/myshixun_570461/f2ef5p798r20191210163135/01.ipynb?download=true - src_url = URI("https://47519.jupyter.educoder.net/nbconvert/notebook/data/workspace/myshixun_tpm3575/01.ipynb?download=true") - response = Net::HTTP.get_response(src_url) - - if response.code.to_i != 200 - raise("获取文件内容失败:#{response.code}") - end - - content = response.body - - c = GitService.update_file(repo_path: @shixun.repo_path, - file_path: "01.ipynb", - message: message, - content: content, - author_name: author_name, - author_email: author_email) - - render plain: 'save: #{c.size}' - end - - - - - - private - def shixun - @shixun = Shixun.find(3575) - end - - end \ No newline at end of file diff --git a/app/controllers/shixuns_controller.rb b/app/controllers/shixuns_controller.rb index efa37066d..ca2dfa86b 100644 --- a/app/controllers/shixuns_controller.rb +++ b/app/controllers/shixuns_controller.rb @@ -716,7 +716,7 @@ class ShixunsController < ApplicationController project_fork(@myshixun, @repo_path, current_user.login) rep_url = Base64.urlsafe_encode64(repo_ip_url @repo_path) uri = "#{cloud_bridge}/bridge/game/openGameInstance" - params = {tpiID: "#{myshixun.id}", tpmGitURL: rep_url, tpiRepoName: myshixun.repo_name.split("/").last} + params = {tpiID: "#{@myshixun.id}", tpmGitURL: rep_url, tpiRepoName: @myshixun.repo_name.split("/").last} interface_post uri, params, 83, "实训云平台繁忙(繁忙等级:83)" end end diff --git a/app/controllers/student_works_controller.rb b/app/controllers/student_works_controller.rb index 637155a1a..dfdce7bd2 100644 --- a/app/controllers/student_works_controller.rb +++ b/app/controllers/student_works_controller.rb @@ -529,8 +529,8 @@ class StudentWorksController < ApplicationController @echart_data = student_efficiency(@homework, @work) @myself_eff = @echart_data[:efficiency_list].find { |item| item.last == @user.id } @myself_consume = @echart_data[:consume_list].find { |item| item.last == @user.id } - filename_ = "#{@use&.student_id}_#{@use&.real_name}_#{@shixun&.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}" - filename = Base64.urlsafe_encode64(filename_.strip) + filename_ = "#{@use&.student_id}_#{@use&.real_name}_#{@shixun&.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}.pdf" + filename = filename_.strip.tr("+/", "-_") stylesheets = %w(shixun_work/shixun_work.css shared/codemirror.css) if params[:export].present? && params[:export] normal_status(0,"正在下载中") diff --git a/app/helpers/export_helper.rb b/app/helpers/export_helper.rb index 12f1ccdae..ef6f3255c 100644 --- a/app/helpers/export_helper.rb +++ b/app/helpers/export_helper.rb @@ -22,21 +22,21 @@ module ExportHelper end end - shixun_homeworks = shixun_homeworks&.includes(score_student_works: :user) + shixun_homeworks = shixun_homeworks&.includes(:score_student_works) common_homeworks = homeworks.search_homework_type(1) #全部普通作业 common_titles = common_homeworks.pluck(:name)+ ["总得分"] - common_homeworks = common_homeworks&.includes(score_student_works: :user) + common_homeworks = common_homeworks&.includes(:score_student_works) group_homeworks = homeworks.search_homework_type(3) #全部分组作业 group_titles = group_homeworks.pluck(:name)+ ["总得分"] - group_homeworks = group_homeworks&.includes(score_student_works: :user) + group_homeworks = group_homeworks&.includes(:score_student_works) task_titles = tasks.pluck(:name) + ["总得分"] - tasks = tasks&.includes(user: :user_extension, score_graduation_works: :user) + tasks = tasks&.includes(:score_graduation_works) exercise_titles = exercises.pluck(:exercise_name) + ["总得分"] - exercises = exercises&.includes(user: :user_extension, score_exercise_users: :user) + exercises = exercises&.includes(:score_exercise_users) total_user_score_array = [] #学生总成绩集合 @@ -168,7 +168,7 @@ module ExportHelper #实训作业 shixun_homeworks.each_with_index do |s,index| - all_student_works = s.score_student_works.select{|work| all_user_ids.include?(work.user_id)} #该实训题的全部用户回答 + all_student_works = s.score_student_works.where(user_id: all_user_ids) #该实训题的全部用户回答 title_no = index.to_i + 1 student_work_to_xlsx(all_student_works,s) shixun_work_display_name = format_sheet_name (title_no.to_s + "." + s.name).strip.first(30) @@ -178,7 +178,7 @@ module ExportHelper #普通作业 common_homeworks.each_with_index do |c,index| - all_student_works = c.score_student_works.select{|work| all_user_ids.include?(work.user_id)} #当前用户的对该作业的回答 + all_student_works = c.score_student_works.where(user_id: all_user_ids) #当前用户的对该作业的回答 title_no = count_1 + index.to_i + 1 student_work_to_xlsx(all_student_works,c) @@ -190,7 +190,7 @@ module ExportHelper #分组作业 group_homeworks.each_with_index do |c,index| - all_student_works = c.score_student_works.select{|work| all_user_ids.include?(work.user_id)} #当前用户的对该作业的回答 + all_student_works = c.score_student_works.where(user_id: all_user_ids) #当前用户的对该作业的回答 title_no = count_1 + count_2 + index.to_i + 1 student_work_to_xlsx(all_student_works,c) work_name = format_sheet_name (title_no.to_s + "." + c.name).strip.first(30) @@ -200,7 +200,7 @@ module ExportHelper #毕设任务 tasks.each_with_index do |c,index| - all_student_works = c.score_graduation_works.select{|work| all_user_ids.include?(work.user_id)} #当前用户的对该作业的回答 + all_student_works = c.score_graduation_works.where(user_id: all_user_ids) #当前用户的对该作业的回答 title_no = count_1 + count_2 + count_3 + index.to_i + 1 graduation_work_to_xlsx(all_student_works,c,current_user) work_name = format_sheet_name (title_no.to_s + "." + c.name).strip.first(30) @@ -210,7 +210,7 @@ module ExportHelper #试卷的导出 exercises.each_with_index do |c,index| - all_student_works = c.score_exercise_users.select{|work| all_user_ids.include?(work.user_id)} #当前用户的对该作业的回答 + all_student_works = c.score_exercise_users.where(user_id: all_user_ids) #当前用户的对该作业的回答 title_no = count_1 + count_2 + count_3 + count_4 + index.to_i + 1 get_export_users(c,course,all_student_works) work_name = format_sheet_name (title_no.to_s + "." + c.exercise_name).strip.first(30) diff --git a/app/services/jupyter_service.rb b/app/services/jupyter_service.rb index 109c8bec6..32c2047c4 100644 --- a/app/services/jupyter_service.rb +++ b/app/services/jupyter_service.rb @@ -36,15 +36,15 @@ module JupyterService end - def _open_game_jupyter(game) + def _open_game_jupyter(myshixun) ## 打开tpi - shixun = game.myshixun.shixun + shixun = myshixun.shixun if shixun.is_jupyter? shixun_tomcat = edu_setting('cloud_bridge') uri = "#{shixun_tomcat}/bridge/jupyter/get" - tpiID = game.myshixun.id + tpiID = myshixun.id params = {tpiID: tpiID, :containers => "#{Base64.urlsafe_encode64(shixun_container_limit(shixun))}"} res = uri_post uri, params @@ -56,20 +56,20 @@ module JupyterService @game_jupyter_port = res['port'] - repo_save_path = game.myshixun.repo_save_path + repo_save_path = myshixun.repo_save_path "https://#{res['port']}.jupyter.educoder.net/notebooks/data/workspace/myshixun_#{tpiID}/#{repo_save_path}/01.ipynb" end end - def jupyter_url_with_game(game) - _open_game_jupyter(game) + def jupyter_url_with_game(myshixun) + _open_game_jupyter(myshixun) end - def jupyter_port_with_game(game) + def jupyter_port_with_game(myshixun) if @game_jupyter_port.to_i <=0 - _open_game_jupyter(shixun) + _open_game_jupyter(myshixun) end @game_jupyter_port end @@ -82,11 +82,11 @@ module JupyterService tpiID = "tpm#{shixun.id}" #https://47526.jupyter.educoder.net/nbconvert/notebook/data/workspace/myshixun_570461/f2ef5p798r20191210163135/01.ipynb?download=true - src_url = URI("https://#{jupyter_port}.jupyter.educoder.net/nbconvert/notebook/data/workspace/myshixun_#{tpiID}/01.ipynb?download=true") - response = Net::HTTP.get_response(src_url) + src_url = "https://#{jupyter_port}.jupyter.educoder.net/nbconvert/notebook/data/workspace/myshixun_#{tpiID}/01.ipynb?download=true" + response = Faraday.get(src_url) - if response.code.to_i != 200 - raise("获取文件内容失败:#{response.code}") + if response.status.to_i != 200 + raise("获取文件内容失败:#{response.status}") end content = response.body @@ -101,25 +101,25 @@ module JupyterService return c.size end - def jupyter_save_with_game(game,jupyter_port) + def jupyter_save_with_game(myshixun,jupyter_port) author_name = current_user.real_name author_email = current_user.git_mail message = "User submitted" - tpiID = game.myshixun.id + tpiID = myshixun.id - repo_save_path = game.myshixun.repo_save_path + repo_save_path = myshixun.repo_save_path - src_url = URI("https://#{jupyter_port}.jupyter.educoder.net/nbconvert/notebook/data/workspace/myshixun_#{tpiID}/#{repo_save_path}/01.ipynb?download=true") - response = Net::HTTP.get_response(src_url) + src_url = "https://#{jupyter_port}.jupyter.educoder.net/nbconvert/notebook/data/workspace/myshixun_#{tpiID}/#{repo_save_path}/01.ipynb?download=true" + response = Faraday.get(src_url) - if response.code.to_i != 200 - raise("获取文件内容失败:#{response.code}") + if response.status.to_i != 200 + raise("获取文件内容失败:#{response.status}") end content = response.body - c = GitService.update_file(repo_path: game.myshixun.repo_path, + c = GitService.update_file(repo_path: myshixun.repo_path, file_path: "01.ipynb", message: message, content: content, diff --git a/public/react/config/webpack.config.prod.js b/public/react/config/webpack.config.prod.js index fbfbf23bc..1fe7a6c93 100644 --- a/public/react/config/webpack.config.prod.js +++ b/public/react/config/webpack.config.prod.js @@ -327,7 +327,7 @@ module.exports = { }, compress: { drop_debugger: true, - drop_console: false + drop_console: true } } }), diff --git a/public/react/src/App.js b/public/react/src/App.js index 7b276c9eb..7d83757aa 100644 --- a/public/react/src/App.js +++ b/public/react/src/App.js @@ -615,6 +615,15 @@ class App extends Component { + {/* jupyter */} + { + return () + } + } + /> + @@ -702,10 +711,6 @@ class App extends Component { (props) => () }/> - - - { - this.props.type==='shixuns'? - ( - item.is_jupyter===true? -
-

- Jupyter -

-
- :"" - ) - :"" - } - + {/*{*/} + {/* this.props.type==='shixuns'?*/} + {/* (*/} + {/* item.is_jupyter===true?*/} +
+

+ Jupyter +

+
+ {/* :""*/} + {/* )*/} + {/* :""*/} + {/*}*/} +
@@ -345,7 +372,13 @@ class ShixunsHome extends Component { {item.tag_name} {/**/} - + { + item.is_jupyter===true? +
+

Jupyter

+ {/**/} +
+ :""}
diff --git a/public/react/src/modules/modals/Bottomsubmit.js b/public/react/src/modules/modals/Bottomsubmit.js index 7c811be26..b128b2afe 100644 --- a/public/react/src/modules/modals/Bottomsubmit.js +++ b/public/react/src/modules/modals/Bottomsubmit.js @@ -10,7 +10,8 @@ class Bottomsubmit extends Component { } cannelfun=()=>{ - window.location.href=this.props.url + // window.location.href= + this.props.history.replace(this.props.url); } diff --git a/public/react/src/modules/tpm/TPMBanner.js b/public/react/src/modules/tpm/TPMBanner.js index c78c05b92..7befed4c4 100644 --- a/public/react/src/modules/tpm/TPMBanner.js +++ b/public/react/src/modules/tpm/TPMBanner.js @@ -748,82 +748,86 @@ class TPMBanner extends Component { - -
this.showonMouseOver()} - onMouseOut={() => this.hideonMouseOut()}> -
学员评分
-
- -
-
this.hideonMouseOut()}> -
- -
-
-
+ { + this.props.is_jupyter===false? +
this.showonMouseOver()} + onMouseOut={() => this.hideonMouseOut()}> +
学员评分
+
+ +
+
this.hideonMouseOut()}> +
+ +
+
+
{star_infos[0]}分 - 总评分 -
- {showradios === true ? - - : ""} -
-
-
-
-
-
- {showradios === true ? - - : ""} -
- - {star_infos[1]}% -
-
-
- {showradios === true ? - - : ""} -
- - {star_infos[2]}% -
-
-
- {showradios === true ? - - : ""} -
- - {star_infos[3]}% -
-
-
- {showradios === true ? - - : ""} -
- - {star_infos[4]}% -
-
-
- {showradios === true ? - - : ""} -
- - {star_infos[5]}% -
-
-
-
-
+ className="font-24 color-yellow-ff lineh-20 mb10 ml20">{star_infos[0]}分 + 总评分 +
+ {showradios === true ? + + : ""} +
+
+
+
+
+
+ {showradios === true ? + + : ""} +
+ + {star_infos[1]}% +
+
+
+ {showradios === true ? + + : ""} +
+ + {star_infos[2]}% +
+
+
+ {showradios === true ? + + : ""} +
+ + {star_infos[3]}% +
+
+
+ {showradios === true ? + + : ""} +
+ + {star_infos[4]}% +
+
+
+ {showradios === true ? + + : ""} +
+ + {star_infos[5]}% +
+
+
+
+
+ +
+ :"" + } -
{ startbtn === false && shixunsDetails.shixun_status != -1 ? diff --git a/public/react/src/modules/tpm/TPMDataset.js b/public/react/src/modules/tpm/TPMDataset.js index 91322d18e..b7f168e0a 100644 --- a/public/react/src/modules/tpm/TPMDataset.js +++ b/public/react/src/modules/tpm/TPMDataset.js @@ -16,7 +16,6 @@ class TPMDataset extends Component { constructor(props) { super(props) this.state = { - datas: [0, 1, 2, 3, 4, 5], value: undefined, columns: [ { @@ -80,10 +79,15 @@ class TPMDataset extends Component { datalist:[], data_sets_count:0, selectedRowKeysdata:[], + loadingstate:false, + checked: false, } } componentDidMount() { + this.setState({ + loadingstate:true, + }) this.getdatas() } @@ -98,18 +102,20 @@ class TPMDataset extends Component { datas.push(i); } - this.setState({ selectedRowKeysdata:mydata, selectedRowKeys: datas, + checked:true, }) // console.log(mydata); // console.log(datas); - - } else { this.setState({ + selectedRowKeysdata:[], selectedRowKeys: [], + checked:false, + + }) } } @@ -143,11 +149,26 @@ class TPMDataset extends Component { collaboratorList: response.data, data_sets_count:response.data.data_sets_count, datalist:datalists, + selectedRowKeysdata:[], + selectedRowKeys: [], + checked:false, }); + } } + setTimeout(() => { + this.setState({ + loadingstate:false, + }) + }, 500) + }).catch((error)=>{ + setTimeout(() => { + this.setState({ + loadingstate:false, + }) + }, 500) console.log(error) }); @@ -181,34 +202,39 @@ class TPMDataset extends Component { collaboratorList: response.data, data_sets_count:response.data.data_sets_count, datalist:datalists, + selectedRowKeysdata:[], + selectedRowKeys: [], + checked:false, }); } } + setTimeout(() => { + this.setState({ + loadingstate:false, + }) + }, 500) }).catch((error)=>{ + setTimeout(() => { + this.setState({ + loadingstate:false, + }) + }, 500) console.log(error) }); } - showModal = (id, status) => { - - }; - - handleOk = (id, editid) => { - - }; - - handleCancel = (e) => { - - }; paginationonChanges = (pageNumber) => { // //console.log('Page: '); this.setState({ page: pageNumber, + loadingstate:true, }) + this.getdatastwo(pageNumber,10); } + onSelectChange = (selectedRowKeys, selectedRows) => { console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows); this.setState( @@ -237,27 +263,37 @@ class TPMDataset extends Component { // 附件相关 START handleChange = (info) => { - debugger if(info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') { let {fileList} = this.state; if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') { - console.log("handleChange1"); + console.log("handleChange1fileLists"); // if(fileList.length===0){ let fileLists = info.fileList; + // console.log(fileLists); this.setState({ // fileList:appendFileSizeToUploadFileAll(fileList), fileList: fileLists, deleteisnot: false }); + } + if(info.file.status === 'done'){ + //done 成功就会调用这个方法 this.getdatas(); - // } + // this.props.showNotification(`上传文件成功`); + + }else if(info.file.status === 'removed'){ + // this.props.showNotification(`上传文件失败`); + + }else if(info.file.status === 'uploading'){ + // this.props.showNotification(`正在上传文件中`); + } } } onAttachmentRemove = (file) => { - debugger + // debugger if(!file.percent || file.percent == 100){ confirm({ title: '确定要删除这个附件吗?', @@ -278,28 +314,48 @@ class TPMDataset extends Component { } deleteRemovedata(){ - debugger - console.log("删除"); - console.log(this.state.selectedRowKeysdata); - const url = `/attachments/destroy_files.json`; - axios.delete(url, { - id:this.state.selectedRowKeysdata, - }) - .then((response) => { - if (response.data) { - const { status } = response.data; - if (status == 0) { - this.props.showNotification(`删除成功`); - this.getdatas() - } - } - }) - .catch(function (error) { - console.log(error); - }); + + if(this.state.selectedRowKeysdata===undefined || this.state.selectedRowKeysdata===null ||this.state.selectedRowKeysdata.length===0){ + + this.props.showNotification(`请选择要删除的文件`); + + return + } + + confirm({ + title: '确定要删除文件吗?', + okText: '确定', + cancelText: '取消', + // content: 'Some descriptions', + onOk: () => { + const url = `/attachments/destroy_files.json`; + axios.delete(url, + { params: { + id:this.state.selectedRowKeysdata, + }} + ) + .then((response) => { + if (response.data) { + const { status } = response.data; + if (status == 0) { + this.props.showNotification(`删除成功`); + + this.getdatas() + } + } + }) + .catch(function (error) { + console.log(error); + }); + }, + onCancel() { + console.log('Cancel'); + }, + }); + } deleteAttachment = (file) => { - console.log(file); + // console.log(file); let id=file.response ==undefined ? file.id : file.response.id const url = `/attachements/destroy_files.json` axios.delete(url, { @@ -333,7 +389,7 @@ class TPMDataset extends Component { render() { const {tpmLoading, shixun, user, match} = this.props; - const {columns, datas, page, limit, selectedRowKeys,mylistansum,fileList,datalist,data_sets_count} = this.state; + const {columns, page, limit, selectedRowKeys,mylistansum,fileList,datalist,data_sets_count,loadingstate} = this.state; const rowSelection = { selectedRowKeys, onChange: this.onSelectChange, @@ -359,7 +415,7 @@ class TPMDataset extends Component { onChange: this.handleChange, onRemove: this.onAttachmentRemove, beforeUpload: (file, fileList) => { - debugger + if (this.state.fileList.length >= 1) { return false } @@ -372,7 +428,6 @@ class TPMDataset extends Component { message: '提示', description: '文件大小必须小于50MB', - } ) } @@ -386,8 +441,6 @@ class TPMDataset extends Component { file:file }) } - - console.log("handleChange2"); return isLt150M; }, } @@ -407,9 +460,14 @@ class TPMDataset extends Component {
-
- 全选 -
+ { + data_sets_count>0? +
+ 全选 +
+ :"" + } +
+ + + { + this.props.identity < 5 && this.state.data && this.state.data.shixun.status == 0 ? + + : "" + } + { + this.props.identity == 1 && this.state.data && this.state.data.shixun.status == 2 ? + : "" + } + { + this.props.identity === 1 && this.state.data && this.state.data.shixun.status == 2 ? + : "" } - - - - -
- }> - - - - - - - - - - -
+
+ }> + + this.getdatas()} + /> + + + this.getdatas()} + /> + + {/*{ this.props.shixunsDetails===undefined?"":this.props.shixunsDetails.is_jupyter===true?"":*/} + {/* */} + {/*}*/} + + +
+ {this.state.delType === 1 ?

是否确认删除 ?

: +

关闭后,
用户不能再开始挑战了是否确认关闭 ?

} +
+
+ 取消 + {this.state.delType === 1 ? 确定 : + 确定} +
+
+
); } diff --git a/public/react/src/modules/tpm/TPMsettings/css/TPMsettings.css b/public/react/src/modules/tpm/TPMsettings/css/TPMsettings.css index a15f7bcf0..9e8858596 100644 --- a/public/react/src/modules/tpm/TPMsettings/css/TPMsettings.css +++ b/public/react/src/modules/tpm/TPMsettings/css/TPMsettings.css @@ -125,4 +125,13 @@ a.newuse_scope-btn { .ant-tabs-extra-content{ margin-top: 18px; +} + +.pdb30{ + padding-bottom: 30px; +} + +.openrenyuan{ + margin-top: 5px !important; + display: inline-block; } \ No newline at end of file diff --git a/public/react/src/modules/tpm/component/TPMNav.js b/public/react/src/modules/tpm/component/TPMNav.js index 6b259c95b..ac5128f26 100644 --- a/public/react/src/modules/tpm/component/TPMNav.js +++ b/public/react/src/modules/tpm/component/TPMNav.js @@ -5,6 +5,10 @@ import { BrowserRouter as Router, Route, Link } from "react-router-dom"; class TPMNav extends Component { render() { + // console.log("componentDidMount"); + // console.log("TPMNavTPMNavTPMNavTPMNav"); + // console.log(this.props); + const { user, match, shixun, secret_repository,is_jupyter} = this.props; let isAdminOrCreator = false; if (user) { @@ -17,6 +21,7 @@ class TPMNav extends Component { // console.log(match.path) // console.log("TPMNavTPMNavTPMNav"); // console.log(is_jupyter); + const is_teacher = this.props&&this.props.current_user&&this.props.current_user.is_teacher?this.props.current_user.is_teacher:""; return (
数据集 + ( + is_teacher===true? + 数据集 + :"" + ) + + :"" + } + + { + this.props.is_jupyter === false ? + 评论 :"" } + { + this.props.is_jupyter === false ? + 排行榜:"" + } + {this.props.identity >2||this.props.identity===undefined?"": + (this.props.is_jupyter === false? + 审核情况 + : + is_teacher===true? + 审核情况 + : + "" + + ) - 评论 - 排行榜 - {this.props.identity >2||this.props.identity===undefined?"":审核情况} + } {this.props.identity >4||this.props.identity===undefined ? "":配置} diff --git a/public/react/src/modules/tpm/jupyter/index.js b/public/react/src/modules/tpm/jupyter/index.js index 031b5c2b9..6a84f964e 100644 --- a/public/react/src/modules/tpm/jupyter/index.js +++ b/public/react/src/modules/tpm/jupyter/index.js @@ -4,38 +4,165 @@ * @Github: * @Date: 2019-12-11 08:35:23 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-11 09:13:09 + * @LastEditTime: 2019-12-12 20:19:48 */ import './index.scss'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import SplitPane from 'react-split-pane'; -import { Button } from 'antd'; - +import { Button, Modal } from 'antd'; +import { + connect +} from 'react-redux'; import UserInfo from '../../developer/components/userInfo'; - +import actions from '../../../redux/actions'; +import LeftPane from './leftPane'; +import RightPane from './rightPane'; function JupyterTPI (props) { + // 获取 identifier 值 + const { + match: { + params = {} + }, + url, + loading, // 保存按钮状态 + dataSets, // 数据集 + jupyter_info, + getJupyterInfo, + syncJupyterCode, + jupyter_tpi_url_state, + // getJupyterTpiDataSet, + getJupyterTpiUrl, + saveJupyterTpi, + changeLoadingState, + changeGetJupyterUrlState + } = props; + + const {identifier} = params; + const [userInfo, setUserInfo] = useState({}); + const [jupyterInfo, setJupyterInfo] = useState({}); + const [updateTip, setUpdateTip] = useState(true); + const [myIdentifier, setMyIdentifier] = useState(''); + useEffect(() => { + /* 先调用 jupyter的 TPI 接口, + * 获取 用户信息, + * 实训的 identifier, 状态, 名称, 是否被修改等信息 + */ + getJupyterInfo(identifier); + }, [identifier]); + + useEffect(() => { + // 设置jupyter信息 + setJupyterInfo(jupyter_info || {}); + const {user, tpm_modified, myshixun_identifier} = jupyter_info; + if (user) { + setUserInfo(user); + } + + if (myshixun_identifier) { + setMyIdentifier(myshixun_identifier); + } + + // 同步代码 + if (tpm_modified && updateTip && myshixun_identifier) { + setUpdateTip(false); + Modal.confirm({ + title: '更新通知', + content: (
+

关卡任务的代码文件有更新啦

+

更新操作将保留已完成的评测记录和成绩

+

还未完成评测的任务代码,请自行保存

+
), + okText: '确定', + cancelText: '取消', + onOk () { + syncJupyterCode(myshixun_identifier, '同步成功'); + } + }) + } + }, [props]); + + // 重置实训 + const handleClickResetTpi = () => { + Modal.confirm({ + title: '重置实训', + content: ( +

+ 你在本文件中修改的内容将丢失,
+ 是否确定重新加载初始代码? +

+ ), + okText: '确定', + cancelText: '取消', + onOk () { + console.log('调用重置代码....', myIdentifier); + if (myIdentifier) { + syncJupyterCode(myIdentifier, '重置成功'); + } + } + }) + } + + // 退出实训 + const handleClickQuitTpi = () => { + // console.log(jupyterInfo); + const { identifier } = jupyterInfo; + props.history.push(`/shixuns/${identifier}/challenges`); + } + + // 重新获取 jupyter url + const handleOnReloadUrl = (id) => { + // console.log('jupyter 信息: ', jupyterInfo); + // 改变加载状态值 + changeGetJupyterUrlState(-1); + getJupyterTpiUrl({identifier: myIdentifier}); + } + + // 保存代码 + const handleOnSave = () => { + // 改变按钮状态 + changeLoadingState(true); + saveJupyterTpi(); + } + return (
- +

- MySQL数据库编程开发实训(基础篇) - 时间 + {jupyterInfo.name} +

{/* sync | poweroff */} - - + +

- 左侧内容 +
-
右侧内容
+
@@ -44,4 +171,35 @@ function JupyterTPI (props) { ); } -export default JupyterTPI; +const mapStateToProps = (state) => { + const { + jupyter_info, + jupyter_tpi_url, + jupyter_data_set, + jupyter_tpi_url_state + } = state.jupyterReducer; + const { loading } = state.commonReducer; + return { + loading, + jupyter_info, + url: jupyter_tpi_url, + dataSets: jupyter_data_set, + jupyter_tpi_url_state + }; +} + +const mapDispatchToProps = (dispatch) => ({ + changeGetJupyterUrlState: (status) => dispatch(actions.changeGetJupyterUrlState(status)), + getJupyterInfo: (identifier) => dispatch(actions.getJupyterInfo(identifier)), + // 重置代码 + syncJupyterCode: (identifier, msg) => dispatch(actions.syncJupyterCode(identifier, msg)), + // getJupyterTpiDataSet: (identifier) => dispatch(actions.getJupyterTpiDataSet(identifier)), + getJupyterTpiUrl: (identifier) => dispatch(actions.getJupyterTpiUrl(identifier)), + saveJupyterTpi: () => dispatch(actions.saveJupyterTpi()), + changeLoadingState: (flag) => dispatch(actions.changeLoadingState(flag)) +}); + +export default connect( + mapStateToProps, + mapDispatchToProps +)(JupyterTPI); diff --git a/public/react/src/modules/tpm/jupyter/index.scss b/public/react/src/modules/tpm/jupyter/index.scss index 7ec98f3b0..a174c5be5 100644 --- a/public/react/src/modules/tpm/jupyter/index.scss +++ b/public/react/src/modules/tpm/jupyter/index.scss @@ -55,7 +55,7 @@ height: 60px; line-height: 60px; background-color: #070F1A; - + padding-left: 30px; .jupyter_title{ display: flex; flex-direction: column; @@ -90,4 +90,12 @@ position: relative; height: calc(100vh - 60px); } + + .update_notice{ + text-align: center; + .update_txt{ + line-height: 18px; + font-size: 14px; + } + } } \ No newline at end of file diff --git a/public/react/src/modules/tpm/jupyter/leftPane/index.js b/public/react/src/modules/tpm/jupyter/leftPane/index.js new file mode 100644 index 000000000..f8cbbeccd --- /dev/null +++ b/public/react/src/modules/tpm/jupyter/leftPane/index.js @@ -0,0 +1,84 @@ +/* + * @Description: + * @Author: tangjiang + * @Github: + * @Date: 2019-12-12 10:34:03 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-12 20:18:46 + */ +import './index.scss'; +import React, { useState, useEffect } from 'react'; +import {Icon, Empty} from 'antd'; +import MyIcon from '../../../../common/components/MyIcon'; + +function LeftPane (props) { + + // 获取数据集 + const { dataSets = [] } = props; + + const emptyCtx = ( +
+ +
+ ); + + // const listCtx = ; + const [renderCtx, setRenderCtx] = useState(() => (emptyCtx)); + + useEffect(() => { + if (dataSets.length > 0) { + console.log('数据集的个数: ', dataSets.length); + const oList = dataSets.map((item, i) => { + return ( +
  • + + {item.title} +
  • + ); + }); + + const oUl = ( +
      + { oList } +
    + ); + + setRenderCtx(oUl); + } + }, [props]); + // 渲染数据集 + // const renderList = () => { + // // 空数据 + // if (dataSets.length === 0) { + // return
    + // + //
    + // } else { + // // 渲染列表 + // const oList = dataSets.map((item, i) => { + // return ( + //
  • + // + // {item.title} + //
  • + // ); + // }); + // return ( + //
      + // { oList } + //
    + // ); + // } + // } + + return ( +
    +

    + 数据集 +

    + { renderCtx } +
    + ) +} + +export default LeftPane; \ No newline at end of file diff --git a/public/react/src/modules/tpm/jupyter/leftPane/index.scss b/public/react/src/modules/tpm/jupyter/leftPane/index.scss new file mode 100644 index 000000000..dfd9f121d --- /dev/null +++ b/public/react/src/modules/tpm/jupyter/leftPane/index.scss @@ -0,0 +1,55 @@ +.jupyter_data_sets_area{ + height: 100%; + .jupyter_h2_title{ + height: 50px; + line-height: 50px; + background-color: #EEEEEE; + padding: 0 30px; + .jupyter_data_icon{ + color: #7286ff; + font-size: 24px; + position: relative; + top: 2px; + transform: scale(1.5); + } + } + + .jupyter_data_list, + .jupyter_empty{ + height: calc(100vh - 110px); + overflow-y: auto; + } + + .jupyter_data_list{ + .jupyter_item{ + line-height:45px; + border-bottom: 1px solid rgba(238,238,238, 1); + padding: 0 30px 0 60px; + overflow: hidden; + text-overflow:ellipsis; + white-space: nowrap; + cursor: pointer; + transition: .3s; + &:hover{ + background-color: rgba(235, 235, 235, .3); + } + .jupyter_icon{ + color: rgb(74, 188, 125); + font-size: 16px; + transform: scale(1.2); + margin-right: 5px; + } + .jupyter_name{ + color: #000; + font-size: 16px; + } + } + } + + .jupyter_empty{ + display: flex; + align-items: center; + justify-content: center; + width: 100%; + } +} \ No newline at end of file diff --git a/public/react/src/modules/tpm/jupyter/rightPane/index.js b/public/react/src/modules/tpm/jupyter/rightPane/index.js new file mode 100644 index 000000000..1282732f4 --- /dev/null +++ b/public/react/src/modules/tpm/jupyter/rightPane/index.js @@ -0,0 +1,90 @@ +/* + * @Description: + * @Author: tangjiang + * @Github: + * @Date: 2019-12-12 15:04:20 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-12 17:41:41 + */ +import './index.scss'; +import React, { useEffect, useState } from 'react'; +import { Spin, Button } from 'antd'; +function RightPane (props) { + const { + status, + url, + onReloadUrl, + onSave, + loading + } = props; + + const [renderCtx, setRenderCtx] = useState(() => loadInit); + // 重新获取 url + const handleClickReload = () => { + onReloadUrl && onReloadUrl(); + } + + const loadInit = ( +
    + +
    + ); + + const loadError = ( +
    +

    + 加载实训出错,是否 + 重新加载 +

    +
    + ); + + // 保存 + const handleClickSubmit = () => { + console.log('调用了保存接口....'); + onSave && onSave(); + } + + useEffect(() => { + if (status === -1) { + setRenderCtx(() => loadInit); + } else if (status === 0 && url) { + setRenderCtx(() => ( + +
    +
    + +
    +
    + +
    +
    + + )); + } else { + setRenderCtx(() => loadError); + } + }, [status, url, loading]); + + return ( +
    + { renderCtx } +
    + ) +} + +export default RightPane; + diff --git a/public/react/src/modules/tpm/jupyter/rightPane/index.scss b/public/react/src/modules/tpm/jupyter/rightPane/index.scss new file mode 100644 index 000000000..edf305623 --- /dev/null +++ b/public/react/src/modules/tpm/jupyter/rightPane/index.scss @@ -0,0 +1,62 @@ +.jupyter_right_pane_area{ + position: relative; + height: calc(100vh - 60px); + // background: pink; + + .jupyter_load_url_error, + .jupyter_loading_init{ + display: flex; + position: relative; + align-items: center; + justify-content: center; + height: 100%; + &::before{ + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + content: ''; + + } + } + + .jupyter_loading_init{ + &::before{ + background-color: rgba(0,0,0,.2); + } + } + + .jupyter_load_url_error{ + // &::before{ + // background-color: rgba(0,0,0,.2); + // } + .jupyter_error_txt{ + position: relative; + z-index: 1; + .jupyter_reload{ + cursor: pointer; + color: #1890ff; + } + } + } + + .jupyter_result{ + height: 100%; + .jupyter_iframe{ + height: calc(100% - 56px); + // background: pink; + .jupyter_iframe_style{ + border: none; + outline: none; + } + } + .jupyter_submit{ + display: flex; + align-items: center; + height: 56px; + justify-content: flex-end; + padding-right: 30px; + } + } +} \ No newline at end of file diff --git a/public/react/src/modules/tpm/newshixuns/Newshixuns.js b/public/react/src/modules/tpm/newshixuns/Newshixuns.js index 8a1be807b..7621a82ca 100644 --- a/public/react/src/modules/tpm/newshixuns/Newshixuns.js +++ b/public/react/src/modules/tpm/newshixuns/Newshixuns.js @@ -4,7 +4,7 @@ import {TPMIndexHOC} from '../TPMIndexHOC'; import {SnackbarHOC} from 'educoder'; -import {Select, Radio, Input, Modal, Button, Form, Tooltip, Upload, Icon,notification} from 'antd'; +import {Select, Radio, Input, Modal, Button, Form, Tooltip, Upload, Icon, notification} from 'antd'; import axios from 'axios'; @@ -145,7 +145,7 @@ class Newshixuns extends Component { }); let newlist = "" e.map((item, key) => { - if(item.props.name!=""){ + if (item.props.name != "") { newlist = newlist + `${item.props.name}` } }) @@ -316,7 +316,7 @@ class Newshixuns extends Component { // 附件相关 START handleChange = (info) => { - if(info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') { + if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') { let {fileList} = this.state; if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') { @@ -334,7 +334,7 @@ class Newshixuns extends Component { } onAttachmentRemove = (file) => { - if(!file.percent || file.percent == 100){ + if (!file.percent || file.percent == 100) { Modal.confirm({ title: '确定要删除这个附件吗?', okText: '确定', @@ -355,13 +355,12 @@ class Newshixuns extends Component { deleteAttachment = (file) => { console.log(file); - let id=file.response ==undefined ? file.id : file.response.id + let id = file.response == undefined ? file.id : file.response.id const url = `/attachments/${id}.json` - axios.delete(url, { - }) + axios.delete(url, {}) .then((response) => { if (response.data) { - const { status } = response.data; + const {status} = response.data; if (status == 0) { // console.log('--- success') @@ -372,7 +371,7 @@ class Newshixuns extends Component { newFileList.splice(index, 1); return { fileList: newFileList, - deleteisnot:true + deleteisnot: true }; }); } @@ -503,8 +502,8 @@ class Newshixuns extends Component { > - - + +
    @@ -567,7 +566,7 @@ class Newshixuns extends Component {