import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import axios from 'axios'; import Snackbar from 'material-ui/Snackbar'; import Fade from 'material-ui/transitions/Fade'; import update from 'immutability-helper' import Dialog, { DialogActions, DialogContent, DialogContentText, DialogTitle, } from 'material-ui/Dialog'; import Button from 'material-ui/Button'; import EvaluateSuccessEffectDisplay from './EvaluateSuccessEffectDisplay' import _ from 'lodash' /* 若干js库 http://inorganik.github.io/countUp.js/ */ /* 切下一关需要更新: LeftViewContainer state.gameAnswer */ import TPIContext from './TPIContext' import { EDU_ADMIN, EDU_SHIXUN_MANAGER, EDU_SHIXUN_MEMBER, EDU_CERTIFICATION_TEACHER , EDU_GAME_MANAGER, EDU_TEACHER, EDU_NORMAL, EDU_BUSINESS, CNotificationHOC } from 'educoder' import { MuiThemeProvider, createMuiTheme, withStyles } from 'material-ui/styles'; import MUIDialogStyleUtil from '../modules/page/component/MUIDialogStyleUtil' const styles = MUIDialogStyleUtil.getTwoButtonStyle() // 主题自定义 const theme = createMuiTheme({ palette: { primary: { main: '#4CACFF', contrastText: 'rgba(255, 255, 255, 0.87)' }, secondary: { main: '#4CACFF' }, // This is just green.A700 as hex. }, }); const testSetsExpandedArrayInitVal = [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false] window.__fetchAllFlag = false; // 是否调用过fetchAll TODO 如何多次使用provider? const $ = window.$ class TPIContextProvider extends Component { constructor(props) { super(props) this.onRunCodeTestFinish = this.onRunCodeTestFinish.bind(this) this.onRunChooseTestFinish = this.onRunChooseTestFinish.bind(this) this.testSetUnlock = this.testSetUnlock.bind(this) this.onTestSetHeaderClick = this.onTestSetHeaderClick.bind(this) this.onShowPrevStage = this.onShowPrevStage.bind(this) this.onShowNextStage = this.onShowNextStage.bind(this) this.readGameAnswer = this.readGameAnswer.bind(this) this.praisePlus = this.praisePlus.bind(this) this.onGamePassed = this.onGamePassed.bind(this) this.onPathChange = this.onPathChange.bind(this) this.showSnackbar = this.showSnackbar.bind(this) this.showDialog = this.showDialog.bind(this) this.onShowUpdateDialog = this.onShowUpdateDialog.bind(this) this.updateDialogClose = this.updateDialogClose.bind(this) // this.showEffectDisplay(); this.state = { loading: true, // 正在加载数据 gDialogOpen: false, currentGamePassed: false, // 当前game评测通过 currentPassedGameGainGold: 0, // 当前通过的game获得的金币数 currentPassedGameGainExperience: 0, // 当前通过的game获得的经验数 user: {}, challenge: {}, shixun_name: '', hide_code: false, showUpdateDialog: false, testSetsExpandedArray: testSetsExpandedArrayInitVal.slice(0), } } showEffectDisplay = (data) => { const dom = document.getElementById('picture_display'); window.$(dom).show(); ReactDOM.unmountComponentAtNode(dom) ReactDOM.render(<EvaluateSuccessEffectDisplay type={"qrcode"} {...data} />, dom); } onShowUpdateDialog() { this.setState({showUpdateDialog: true}) } // updateNowSuccess true 立即更新成功 // TODO updateDialogClose方法名不对, 改为updateDialogCallback updateDialogClose(nextUpdateSuccess, updateNowSuccess) { const { myshixun } = this.state; if (nextUpdateSuccess) { myshixun.system_tip = true; } let { tpm_cases_modified, tpm_modified, tpm_script_modified } = this.state; if (updateNowSuccess) { tpm_cases_modified = false; tpm_modified = false; tpm_script_modified = false; } this.setState({ myshixun, tpm_cases_modified, tpm_modified, tpm_script_modified, showUpdateDialog: false }) } componentWillUnmount() { this.costTimeInterval && window.clearInterval(this.costTimeInterval) } componentDidMount() { // TODO 登录状态的判断? // request // var shixunId = this.props.match.params.shixunId; var stageId = this.props.match.params.stageId; window.__fetchAllFlag = false; this.fetchAll(stageId); this.costTimeInterval = window.setInterval(()=> { const { game } = this.state; if (!game || game.status === 2) { // 已完成的任务不需要计时 return; } if (game.cost_time || game.cost_time === 0) { // game.cost_time += 1; this.setState({ game: update(game, {cost_time: { $set: (game.cost_time+1) }}) }) } }, 1000) // 页面离开时存下用户的任务耗时 window.$(window).unload( ()=>{ this._updateCostTime(); }); } // force 评测通过后,异步执行该方法,强制同步costTime到服务端 _updateCostTime(async = false, force) { const { game, loading } = this.state; // TODO 还有一种情况,通关后cost_time计时停止,没法通过这个判断 if (!force && (loading || !game || game.status === 2)) { return; // 已完成的任务不需要处理 } let testPath = '' if (window.location.port == 3007) { testPath = 'http://pre-newweb.educoder.net' } // var url = `${testPath}/api/v1/games/${ game.identifier }/cost_time` var url = `${testPath}/api/tasks/${ game.identifier }/cost_time` window.$.ajax({ type: 'get', url: url, async: async, //IMPORTANT, the call will be synchronous data: { time: game.cost_time } }).done((data) => { console.log('complete'); }); } onGamePassed(passed) { const { game } = this.state // 随便给个分,以免重新评测时又出现评星组件(注意:目前game.star没有显示在界面上,如果有则不能这么做) // game.star = 6; this.setState({ game: update(game, {star: { $set: 6 }}), currentGamePassed: !!passed }) } onTestSetHeaderClick(index) { // let { testSetsExpandedArray } = this.state; let testSetsExpandedArray; // 一次只打开一个 if (this.state.testSetsExpandedArray[index] === false) { testSetsExpandedArray = testSetsExpandedArrayInitVal.slice(0); } else { testSetsExpandedArray = this.state.testSetsExpandedArray.slice(0); } testSetsExpandedArray[index] = !testSetsExpandedArray[index]; this.setState({ testSetsExpandedArray, }) } onShowPrevStage() { } onShowNextStage() { window.__fetchAllFlag = false; console.log('onShowNextStage.........') // this.fetchAll('vznhx7mctwfq') } componentWillReceiveProps(newProps, oldProps) { var newStageId = newProps.match.params.stageId; if (!this.props || newStageId !== this.props.match.params.stageId) { window.__fetchAllFlag = false; this.fetchAll(newStageId) } } // praise_tread/praise_plus?obj_id=569&obj_type=Challenge&horizontal=true&game_praise=true /* TODO 旧的接口在未登录时的返回值 //获取登录页面地址 var signinPath = '/'; var htmlvalue = '<div class="task-popup" style="width:480px;"><div class="task-popup-title clearfix"><h3 class="fl color-grey3">提示</h3></div>'+ '<div class="task-popup-content"><p class="task-popup-text-center font-16 mt10 mb10">您还没有登录,请登录后再执行此操作,谢谢!</p></div><div class="task-popup-right-sure clearfix">'+ '<a href="javascript:void(0);" onclick="hideModal();" class="task-btn">取消</a><a href="' + signinPath + '" class="task-btn task-btn-orange ml15">登录</a></div></div>'; pop_box_new(htmlvalue, 480, 182); */ praisePlus() { const { challenge, game } = this.state; let praise = true; const url = `/tasks/${game.identifier}/plus_or_cancel_praise.json` // const url = `/praise_tread/praise_plus?obj_id=${challenge.id}&obj_type=Challenge&horizontal=${praise}&game_praise=true` axios.post(url) .then((response) => { if (response.data) { const { praise_count, praise } = response.data; // challenge.praise_count = praise_tread_count; // challenge.user_praise = praise; this.setState({ challenge: update(challenge, { praise_count: { $set: praise_count }, user_praise: { $set: praise }, }) }) } }) .catch(function (error) { console.log(error); }); } onPathChange(index) { let { challenge } = this.state; // challenge = Object.assign({}, challenge) // challenge.pathIndex = index; this.setState({ challenge: update(challenge, {pathIndex: { $set: index }}), }) // TODO load new path content } updateChallengePath = (path) => { const challenge = this.state.challenge; if (challenge.path === path) { return; } const { myshixun } = this.state; // myshixun.system_tip = false; challenge.path = path; const newChallenge = this.handleChallengePath(challenge); this.setState({ challenge: newChallenge, myshixun: update(myshixun, {system_tip: { $set: false }}), }) } handleChallengePath(challenge) { if (challenge.path && typeof challenge.path === "string") { // 多path的处理 let path = challenge.path.split(';'); _.remove(path, (item)=> !item) if (path.length > 1) { challenge.path = path; challenge.multiPath = true; } else { challenge.path = challenge.path.replace(';', '').trim() // 多path 改为单path 出现了 aaa.java;的情况 challenge.multiPath = false; } } challenge.pathIndex = 0; return challenge; } newResData2OldResData(newResData) { newResData.latest_output = newResData.last_compile_output // newResData.power newResData.record = newResData.record_onsume_time // 老版用的hide_code newResData.hide_code = newResData.shixun.hide_code; newResData.image_url = newResData.user.image_url newResData.grade = newResData.user.grade newResData.user_url = newResData.user.user_url newResData.username = newResData.user.name newResData.output_sets = {} // newResData.output_sets.had_test_count = newResData.test_sets_count newResData.output_sets.test_sets = newResData.test_sets // JSON.stringify() newResData.output_sets.test_sets_count = newResData.test_sets_count // newResData.output_sets.had_passed_testsests_error_count = newResData.sets_error_count newResData.output_sets.had_passed_testsests_error_count = newResData.test_sets_count - newResData.sets_error_count // allowed_hidden_testset // sets_error_count // test_sets_count // test_sets // had_passed_testsests_error_count // test_sets // test_sets return newResData } // 将若干数据重新组织一下 _handleResponseData(resData_arg) { const resData = this.newResData2OldResData(Object.assign({}, resData_arg)) let challenge = resData.challenge; challenge.isHtml = false; challenge.isWeb = false; challenge.isAndroid = false; challenge.showLanguagePictrue = false; challenge.hasAnswer = resData.has_answer; let output_sets = resData.output_sets; if (resData.st === 0) { // 代码题 challenge = this.handleChallengePath(challenge) const mirror_name = (resData.mirror_name && resData.mirror_name.join) ? resData.mirror_name.join(';') : (resData.mirror_name || ''); if (mirror_name.indexOf('Html') !== -1) { challenge.isHtml = true; challenge.showLanguagePictrue = true; } else if (mirror_name.indexOf('Web') !== -1 || mirror_name.indexOf('JFinal') !== -1) { challenge.isWeb = true; } else if (mirror_name.indexOf('Android') !== -1) { challenge.isAndroid = true; } if (output_sets && output_sets.test_sets && typeof output_sets.test_sets == 'string') { const test_sets_array = JSON.parse("[" + output_sets.test_sets + "]"); output_sets.test_sets_array = test_sets_array; } else { output_sets.test_sets_array = output_sets.test_sets } } else { // 选择题 // 选择题题干markdown初始化 const $ = window.$ window.setTimeout(()=>{ var lens = $("#choiceRepositoryView textarea").length; for(var i = 1; i <= lens; i++){ window.editormd.markdownToHTML("choose_subject_" + i, { htmlDecode: "style,script,iframe", // you can filter tags decode taskList: true, tex: true, // 数学公式 // flowChart: true, // 默认不解析 // sequenceDiagram: true // 默认不解析 }); } }, 400) } challenge.user_praise = resData.user_praise; challenge.praise_count = resData.praise_count; challenge.showWebDisplayButton = false; resData.challenge = challenge; // 将一些属性写到game上 let game = resData.game; game.prev_game = resData.prev_game; game.next_game = resData.next_game; if (game.status == 2) { // 已通关 game.isPassThrough = true } resData.game = game; const { tpm_cases_modified, tpm_modified, tpm_script_modified, myshixun } = resData; if (myshixun.system_tip) { // system_tip为true的时候 不弹框提示用户更新 resData.showUpdateDialog = false } else { let needUpdateScript = (tpm_modified || tpm_script_modified) && challenge.st === 0; resData.showUpdateDialog = needUpdateScript || tpm_cases_modified } /** email: "721773699@qq.com" grade: 213996 identity: 1 image_url: "avatars/User/1" login: "innov" name: "Coder" user_url: "/users/innov" */ let user = resData.user; user.username = resData.user.name; user.user_url = `/users/${resData.user.login}`; // user.image_url = resData.image_url; user.is_teacher = resData.is_teacher; resData.user = user; this._handleUserAuthor(resData) // TODO 测试 // resData.power = 0; this.setState({ ...resData, currentGamePassed: false, loading: false, testSetsExpandedArray: testSetsExpandedArrayInitVal.slice(0), }) window.document.title = resData.shixun.name window.__myshixun = resData.myshixun; // tpi_html_show需要用到 } _handleUserAuthor(resData) { // tpi tpm权限控制 // const EDU_ADMIN = 1 // 超级管理员 // const EDU_SHIXUN_MANAGER = 2 // 实训管理员 // const EDU_SHIXUN_MEMBER = 3 // 实训成员 // const EDU_CERTIFICATION_TEACHER = 4 // 平台认证的老师 // const EDU_GAME_MANAGER = 5 // TPI的创建者 // const EDU_TEACHER = 6 // 平台老师,但是未认证 // const EDU_NORMAL = 7 // 普通用户 /** EDU_ADMIN = 1 # 超级管理员 EDU_BUSINESS = 2 # 运营人员 EDU_SHIXUN_MANAGER = 3 # 实训管理员 EDU_SHIXUN_MEMBER = 4 # 实训成员 EDU_CERTIFICATION_TEACHER = 5 # 平台认证的老师 EDU_GAME_MANAGER = 6 # TPI的创建者 EDU_TEACHER = 7 # 平台老师,但是未认证 EDU_NORMAL = 8 # 普通用户 */ // myshixun_manager power is_teacher resData.power = 0 resData.myshixun_manager = false // resData.is_teacher = false if (resData.user.identity === EDU_ADMIN) { resData.power = 1 resData.myshixun_manager = true } else if (resData.user.identity === EDU_BUSINESS) { resData.power = 1 resData.myshixun_manager = true } else if (resData.user.identity === EDU_SHIXUN_MANAGER) { resData.power = 1 resData.myshixun_manager = true } else if (resData.user.identity === EDU_SHIXUN_MEMBER) { resData.power = 1 resData.myshixun_manager = true } else if (resData.user.identity === EDU_CERTIFICATION_TEACHER) { resData.power = 1 // 已认证老师允许跳关 resData.myshixun_manager = true // resData.is_teacher = true } else if (resData.user.identity === EDU_TEACHER) { // resData.is_teacher = true } else if (resData.user.identity === EDU_NORMAL) { } return resData } fetchAll(stageId, noTimeout) { if (window.__fetchAllFlag == true ) { console.log('TPIContextProvider call fetchAll repeatly!') return; } // 切换关卡的时候,同步costTime this._updateCostTime(true); if (!stageId) { // stageId = 'zl6kx8f7vfpo'; // http://localhost:3000/myshixuns/so5w6iap97/stages/zl6kx8f7vfpo } // var url = `/api/v1/games/${stageId}` var url = `/tasks/${stageId}.json` // {"status":1,"message":"undefined method `authenticate!' for #<Grape::Endpoint:0xc8c91c0>"} window.__fetchAllFlag = true; this.setState({ loading: true, currentGamePassed: false, // 切换game时重置passed字段 }) // test // var data = {"st":0,"discusses_count":0,"game_count":3,"record_onsume_time":0.36,"prev_game":null,"next_game":"7p9xwo2hklqv","praise_count":0,"user_praise":false,"time_limit":20,"tomcat_url":"http://47.96.157.89","is_teacher":false,"myshixun_manager":true,"game":{"id":2192828,"myshixun_id":580911,"user_id":57844,"created_at":"2019-09-03T15:50:49.000+08:00","updated_at":"2019-09-03T15:51:05.000+08:00","status":2,"final_score":0,"challenge_id":10010,"open_time":"2019-09-03T15:50:49.000+08:00","identifier":"hknvz4oaw825","answer_open":0,"end_time":"2019-09-03T15:51:04.000+08:00","retry_status":0,"resubmit_identifier":null,"test_sets_view":false,"picture_path":null,"accuracy":1.0,"modify_time":"2019-09-03T15:23:33.000+08:00","star":0,"cost_time":14,"evaluate_count":1,"answer_deduction":0},"challenge":{"id":10010,"shixun_id":3516,"subject":"1.1 列表操作","position":1,"task_pass":"[TOC]\n\n---\n\n####任务描述\n\n\n数据集a包含1-10共10个整数,请以a为输入数据,编写python程序,实现如下功能:\n①\t用2种方法输出a中所有奇数\n②\t输出大于3,小于7的偶数\n③\t用2种方法输出[1,2,3,…10,11,…20]\n④\t输出a的最大值、最小值。\n⑤\t用2种方法输出[10,9,…2,1]\n⑥\t输出[1,2,3,1,2,3,1,2,3,1,2,3]\n\n\n####相关知识\n\n\n请自行学习相关知识\n\n\n---\n开始你的任务吧,祝你成功!","score":100,"path":"1-1-stu.py","st":0,"web_route":null,"modify_time":"2019-09-03T15:23:33.000+08:00","exec_time":20,"praises_count":0},"shixun":{"id":3516,"name":"作业1——Python程序设计","user_id":77620,"gpid":null,"visits":23,"created_at":"2019-09-03T14:18:17.000+08:00","updated_at":"2019-09-03T15:58:16.000+08:00","status":0,"language":null,"authentication":false,"identifier":"6lzjig58","trainee":1,"major_id":null,"webssh":2,"homepage_show":false,"hidden":false,"fork_from":null,"can_copy":true,"modify_time":"2019-09-03T14:18:17.000+08:00","reset_time":"2019-09-03T14:18:17.000+08:00","publish_time":null,"closer_id":null,"end_time":null,"git_url":null,"vnc":null,"myshixuns_count":3,"challenges_count":3,"use_scope":0,"mirror_script_id":20,"image_text":null,"code_hidden":false,"task_pass":true,"exec_time":20,"test_set_permission":true,"sigle_training":false,"hide_code":false,"multi_webssh":false,"excute_time":null,"repo_name":"p09218567/6lzjig58","averge_star":5.0,"opening_time":null,"users_count":1,"forbid_copy":false,"pod_life":0},"myshixun":{"id":580911,"shixun_id":3516,"is_public":true,"user_id":57844,"gpid":null,"created_at":"2019-09-03T15:50:49.000+08:00","updated_at":"2019-09-03T15:59:04.000+08:00","status":0,"identifier":"k36hm4rwav","commit_id":"f25e1713882156480fc45ce0af57eff395a5037f","modify_time":"2019-09-03T14:18:17.000+08:00","reset_time":"2019-09-03T14:18:17.000+08:00","system_tip":false,"git_url":null,"onclick_time":"2019-09-03T15:50:49.000+08:00","repo_name":"p53276410/k36hm4rwav20190903155049"},"user":{"user_id":57844,"login":"p53276410","name":"文振乾","grade":24624,"identity":1,"image_url":"avatars/User/57844","school":"EduCoder团队"},"tpm_modified":true,"tpm_cases_modified":false,"mirror_name":["Python3.6"],"has_answer":false,"test_sets":[{"is_public":true,"result":true,"input":"","output":"result of a:\n[1, 3, 5, 7, 9]\n[1, 3, 5, 7, 9]\nresult of b:\n[2, 4, 6, 8, 10]\nresult of c:\n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]\n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]\nresult of d:\nThe minimum is:1\nThe maxium is:10\nresult of e:\n[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]\n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\nresult of f:\n[10, 9, 8, 10, 9, 8, 10, 9, 8, 10, 9, 8]\n","actual_output":"result of a:\r\n[1, 3, 5, 7, 9]\r\n[1, 3, 5, 7, 9]\r\nresult of b:\r\n[2, 4, 6, 8, 10]\r\nresult of c:\r\n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]\r\n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]\r\nresult of d:\r\nThe minimum is:1\r\nThe maxium is:10\r\nresult of e:\r\n[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]\r\n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\r\nresult of f:\r\n[10, 9, 8, 10, 9, 8, 10, 9, 8, 10, 9, 8]\r\n","compile_success":1,"ts_time":0.05,"ts_mem":8.77}],"allowed_unlock":true,"last_compile_output":"compile successfully","test_sets_count":1,"sets_error_count":0} // data.test_sets[0].actual_output = data.test_sets[0].actual_output.replace(/\r\n/g, '\n') // data.test_sets[0].output = data.test_sets[0].output.replace(/\r\n/g, '\n') // console.log(JSON.stringify(data)) // data.shixun.vnc = true // data.vnc_url= "http://47.96.157.89:41158/vnc_lite.html?password=headless" // this._handleResponseData(data) // return axios.get(url, { // https://stackoverflow.com/questions/48861290/the-value-of-the-access-control-allow-origin-header-in-the-response-must-not-b // withCredentials: true, }) .then((response) => { // {"status":1,"message":"Unauthorized. \u7528\u6237\u8ba4\u8bc1\u5931\u8d25."} window.__fetchAllFlag = false; if (response.data.status == 403) { window.location.href = "/403"; return; } if (response.data.status == 404) { // 如果第一次发生404,则隔1s后再调用一次本接口;(因为ucloud主从同步可能有延迟) if (!noTimeout) { setTimeout(() => { this.fetchAll(stageId, true) }, 1000) return; } window.location.href = '/myshixuns/not_found' return; } this._handleResponseData(response.data) }) .catch(function (error) { console.log(error); }); } readGameAnswer(resData) { // game.final_score = resData.final_score; if (resData.final_score) { var game = this.state.game; this.setState({ game: update(game, {final_score: { $set: resData.final_score }}), grade: resData.grade }) } else { this.setState({ grade: resData.grade }) } } closeTaskResultLayer() { this.setState({ game: (this.state.game.status == 2 ? update(this.state.game, { isPassThrough: { $set: true }, }) : this.state.game) , currentGamePassed: false }) } onRunChooseTestFinish(response) { const { test_sets, challenge_chooses_count, choose_correct_num, grade, experience, gold, had_submmit, next_game } = response; response.had_submmit = true; // 是否已提交 const { game } = this.state; let currentGamePassed = false if (challenge_chooses_count === choose_correct_num) { game.status = 2; // game.isPassThrough = true game.next_game = next_game; currentGamePassed = true; this._updateCostTime(true, true); } this.setState({ choose_test_cases: response, grade: grade, game, next_game, currentGamePassed: currentGamePassed, currentPassedGameGainGold: gold, currentPassedGameGainExperience: experience, }) } initDisplayInterval = () => { const challenge = this.state.challenge if (this.showWebDisplayButtonTimeout) { window.clearTimeout(this.showWebDisplayButtonTimeout) } this.showWebDisplayButtonTimeout = window.setTimeout(() => { this.setState({ challenge: update(challenge, { showWebDisplayButton: { $set: false }, }) }) this.showWebDisplayButtonTimeout = null }, 61 * 1000) let remain = 60 if (this.displayInterval) { window.clearInterval(this.displayInterval) } this.displayInterval = window.setInterval(() => { const button = $('#showWebDisplayButton'); if (button.length) { button.html(`查看效果(${remain})`) if (remain == 0) { button.html('查看效果') } } if (remain == 0) { window.clearInterval(this.displayInterval) this.displayInterval = null return; } remain -= 1; }, 1000) } language_display(data) { const { game, tomcat_url } = this.state; const challenge = Object.assign({}, this.state.challenge) if(challenge.isWeb && data.port != -1) { // var $result = $("#php_display"); challenge.showWebDisplayButton = true; // ActionView处是否出现查看效果按钮 this.initDisplayInterval() const path = challenge.web_route || challenge.path const webDisplayUrl = `${tomcat_url}:${data.port}/${path}` challenge.webDisplayUrl = webDisplayUrl challenge.showLanguagePictrue = true; // 评测通过弹出层是否出现查看效果按钮 } // else if(challenge.isAndroid && data.picture != 0){ // // https://www.educoder.net/shixuns/qrcode?game_id=218589&_=1525571882782 // $.ajax({ // url: `/shixuns/qrcode?game_id=${game.id}`, // dataType: 'script' // }); // challenge.showLanguagePictrue = true; // } else if(data.picture != 0){ // 对应服务端erb文件为 _picture_display.html.erb // $.ajax({ // url: "/users/picture_show?game_id="+data.picture, // cache: false, // dataType: 'script' // }); /** { "type": "image", "orignal_picture": [], "user_picture": [], "answer_picture": [] } */ const url = `/tasks/${game.identifier}/picture_display.json` axios.get(url) .then((response) => { // response.data.type qrcode_str this.showEffectDisplay(response.data) }) challenge.showLanguagePictrue = true; } this.setState({ challenge }) } onRunCodeTestFinish(response) { console.log('onRunCodeTestFinish', response) const { test_sets, test_sets_count, test_sets_hidden_count, test_sets_public_count , had_test_count, had_passed_testsests_error_count, had_passed_testsests_hidden_count , had_passed_testsests_public_count, final_score, gold, experience, latest_output, status , had_done, score, tag_count, power, record, next_game, grade, picture, sets_error_count, last_compile_output, record_consume_time} = response; const { game } = this.state; const currentGamePassed = this.props.game !== 2 && status === 2 // 评测通过了,立即同步costTime currentGamePassed && this._updateCostTime(true, true); const output_sets = { "test_sets": test_sets, "test_sets_array": test_sets, "had_test_count": had_test_count || test_sets_count, "test_sets_count": test_sets_count, // "had_passed_testsests_error_count": had_passed_testsests_error_count, "had_passed_testsests_error_count": test_sets_count - sets_error_count, "test_sets_hidden_count": test_sets_hidden_count, "test_sets_public_count": test_sets_public_count, "had_passed_testsests_hidden_count": had_passed_testsests_hidden_count, "had_passed_testsests_public_count": had_passed_testsests_public_count }; // if (output_sets && output_sets.test_sets) { // const test_sets_array = JSON.parse("[" + output_sets.test_sets + "]"); // output_sets.test_sets_array = test_sets_array; // } // 检查是否编译通过 let compileSuccess = false; if (test_sets && test_sets.length) { test_sets.some((item) => { if (item.compile_success) { compileSuccess = true; return true; } }) } compileSuccess && this.language_display(response); if (currentGamePassed) { game.status = 2; // game.isPassThrough = true game.next_game = next_game; } else { this.showDialog({ contentText: <div> <div>评测未通过</div> <div>详情请参见“测试结果”</div> </div>, isSingleButton: true }) } this.setState({ testSetsExpandedArray: testSetsExpandedArrayInitVal.slice(0), // 重置测试集展开状态 currentGamePassed, currentPassedGameGainGold: gold, currentPassedGameGainExperience: experience, output_sets, game, next_game, latest_output: last_compile_output, record: record_consume_time, grade, had_done, }) } resetTestSetsExpandedArray = () => { this.setState({ testSetsExpandedArray: testSetsExpandedArrayInitVal.slice(0), // 重置测试集展开状态 }) } testSetUnlock() { const { game, challenge } = this.state; const url = `/tasks/${game.identifier}/check_test_sets.json` axios.get(url, { // withCredentials: true, }) .then((response) => { // TODO status -2 重复操作,直接解锁 if (response.data.test_sets == -1) { console.error('testSetUnlock失败!') this.showSnackbar(response.data.message) return; } else { // 被扣除的金币,是负数 const deltaScore = -challenge.score * 5; // output_sets let { output_sets } = this.state; output_sets = Object.assign({}, output_sets); // const test_sets_array = JSON.parse("[" + response.data.test_sets + "]"); output_sets.test_sets_array = response.data.test_sets; this.setState({ output_sets: output_sets, grade: this.state.grade + deltaScore, game : update(game, {test_sets_view: { $set: true }}), testSetsExpandedArray: testSetsExpandedArrayInitVal.slice(0) }) this.handleGdialogClose(); } }) .catch(function (error) { console.log(error); }); } handleSnackbarClose() { this.setState({ snackbarOpen: false, snackbarVertical: '', snackbarHorizontal: '', }) } // 全局的snackbar this.props.showSnackbar调用即可 showSnackbar(text, vertical, horizontal) { this.setState({ snackbarOpen: true, snackbarText: text, snackbarVertical: vertical, snackbarHorizontal: horizontal, }) } /* TODO 写成HOC组件,更好复用 全局的Dialog this.props.showDialog调用即可 @param contentText dialog显示的提示文本 @param callback 确定按钮回调方法 @param moreButtonsRender 除了“确定”、“取消”按钮外的其他按钮 @param okButtonText “确定”按钮显示文本,如 继续查看 */ showDialog(params) { const { contentText, callback, moreButtonsRender, okButtonText, isSingleButton } = params; this.dialogOkCallback = callback; this.moreButtonsRender = moreButtonsRender this.okButtonText = okButtonText; this.isSingleButton = isSingleButton; this.setState({ gDialogOpen: true, gDialogContentText: contentText }) } onGdialogOkBtnClick() { this.dialogOkCallback && this.dialogOkCallback(); // this.setState({ // gDialogOpen: true // }) } handleGdialogClose = () => { this.setState({ gDialogOpen: false }) } render() { const { classes } = this.props; return ( <TPIContext.Provider value={{ ...this.props, ...this.state, resetTestSetsExpandedArray: this.resetTestSetsExpandedArray, onRunCodeTestFinish: this.onRunCodeTestFinish, onRunChooseTestFinish: this.onRunChooseTestFinish, testSetUnlock: this.testSetUnlock, onTestSetHeaderClick: this.onTestSetHeaderClick, readGameAnswer: this.readGameAnswer, onShowPrevStage: this.onShowPrevStage, onShowNextStage: this.onShowNextStage, praisePlus: this.praisePlus, onGamePassed: this.onGamePassed, closeTaskResultLayer: () => this.closeTaskResultLayer(), onPathChange: this.onPathChange, updateChallengePath: this.updateChallengePath, showSnackbar: this.showSnackbar, showDialog: this.showDialog, handleGdialogClose: () => this.handleGdialogClose(), onShowUpdateDialog: this.onShowUpdateDialog, updateDialogClose: this.updateDialogClose, match: this.props.match }} > <Dialog id="tpi-dialog" open={this.state.gDialogOpen} disableEscapeKeyDown={true} onClose={() => this.handleGdialogClose()} > <DialogTitle id="alert-dialog-title">{"提示"}</DialogTitle> <DialogContent id="dialog-content"> <DialogContentText id="alert-dialog-description" style={{textAlign: 'center'}}> {this.state.gDialogContentText} </DialogContentText> </DialogContent> {/* mb20 加了有样式问题 */} <DialogActions className={""} id="dialog-actions"> { this.isSingleButton ? <div className="task-popup-submit clearfix" style={{ textAlign: 'center', 'margin-bottom': '14px'}}> <a className="task-btn task-btn-orange" onClick={this.handleGdialogClose} >知道啦</a> </div> : <React.Fragment> <Button onClick={() => this.handleGdialogClose()} color="primary" className={`${classes.button} ${classes.buttonGray} ${classes.borderRadiusNone}`}> 关闭 </Button> <Button variant="raised" className={`${classes.button} ${classes.borderRadiusNone}`} onClick={() => this.onGdialogOkBtnClick() } color="primary" autoFocus> { this.okButtonText ? this.okButtonText : '确定' } </Button> </React.Fragment> } {this.moreButtonsRender && this.moreButtonsRender()} </DialogActions> </Dialog> <Snackbar className={"rootSnackbar"} open={this.state.snackbarOpen} autoHideDuration={3000} anchorOrigin={{ vertical: this.state.snackbarVertical || 'top' , horizontal: this.state.snackbarHorizontal || 'center' }} onClose={() => this.handleSnackbarClose()} transition={Fade} SnackbarContentProps={{ 'aria-describedby': 'message-id', }} resumeHideDuration={2000} message={<span id="message-id">{this.state.snackbarText}</span>} /> {this.props.children} </TPIContext.Provider> ) } } export default CNotificationHOC() (withStyles(styles) (TPIContextProvider));