diff --git a/public/react/src/context/TPIContextProvider.js b/public/react/src/context/TPIContextProvider.js index e49c6d9d6..10e9c07e2 100644 --- a/public/react/src/context/TPIContextProvider.js +++ b/public/react/src/context/TPIContextProvider.js @@ -892,6 +892,7 @@ pop_box_new(htmlvalue, 480, 182); this.handleGdialogClose()} > {"提示"} diff --git a/public/react/src/modules/comment/Comments.js b/public/react/src/modules/comment/Comments.js index c07bd5de2..1b2e2b45a 100644 --- a/public/react/src/modules/comment/Comments.js +++ b/public/react/src/modules/comment/Comments.js @@ -505,6 +505,7 @@ class Comments extends Component { : {"提示"} @@ -531,6 +532,7 @@ class Comments extends Component { {"奖励设置"} diff --git a/public/react/src/modules/common/RewardDialog.js b/public/react/src/modules/common/RewardDialog.js index cb49e48d0..6e0803ead 100644 --- a/public/react/src/modules/common/RewardDialog.js +++ b/public/react/src/modules/common/RewardDialog.js @@ -1,88 +1,89 @@ -import React, { Component } from 'react'; - -import Dialog, { - DialogActions, - DialogContent, - DialogContentText, - DialogTitle, -} from 'material-ui/Dialog'; -import Button from 'material-ui/Button'; -import { FormControl, FormHelperText } from 'material-ui/Form'; -import Input, { InputLabel } from 'material-ui/Input'; - - - -class RewardDialog extends Component { - constructor(props) { - super(props) - - this.handleGoldRewardDialogClose = this.handleGoldRewardDialogClose.bind(this) - - this.state = { - // goldRewardDialogOpen: false, - goldRewardInput: '', - } - } - - showGoldRewardDialog(comment, childComment) { - if (comment.admin === true) { - this.comment = comment; - this.childComment = childComment; - this.setState({goldRewardDialogOpen: true}) - } - } - - handleGoldRewardDialogClose() { - this.props.setRewardDialogVisible(false) - } - onGoldRewardDialogOkBtnClick() { - console.log('onGoldRewardDialogOkBtnClick') - const { goldRewardInput } = this.state; - if (!goldRewardInput || goldRewardInput === '0' || goldRewardInput.indexOf('-') !== -1) { - this.setState({ goldRewardInputError: true}) - return; - } else { - this.props.setRewardDialogVisible( false ) - this.props.rewardCode(goldRewardInput) - } - } - onGoldRewardInputChange(event) { - this.setState({ goldRewardInput: event.target.value, goldRewardInputError: false }); - } - - render() { - const { goldRewardDialogOpen } = this.props; - const { goldRewardInputError } = this.state; - const goldRewardInputErrorObj = goldRewardInputError ? {'error': 'error'} : {} - - return ( - - {"奖励设置"} - - - - 请输入奖励的金币数量 - this.onGoldRewardInputChange(e)} /> - { goldRewardInputError ? 奖励金币不能为空或负数 : ''} - - - {/* */} - - - - - - - ); - } -} - -export default RewardDialog; +import React, { Component } from 'react'; + +import Dialog, { + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, +} from 'material-ui/Dialog'; +import Button from 'material-ui/Button'; +import { FormControl, FormHelperText } from 'material-ui/Form'; +import Input, { InputLabel } from 'material-ui/Input'; + + + +class RewardDialog extends Component { + constructor(props) { + super(props) + + this.handleGoldRewardDialogClose = this.handleGoldRewardDialogClose.bind(this) + + this.state = { + // goldRewardDialogOpen: false, + goldRewardInput: '', + } + } + + showGoldRewardDialog(comment, childComment) { + if (comment.admin === true) { + this.comment = comment; + this.childComment = childComment; + this.setState({goldRewardDialogOpen: true}) + } + } + + handleGoldRewardDialogClose() { + this.props.setRewardDialogVisible(false) + } + onGoldRewardDialogOkBtnClick() { + console.log('onGoldRewardDialogOkBtnClick') + const { goldRewardInput } = this.state; + if (!goldRewardInput || goldRewardInput === '0' || goldRewardInput.indexOf('-') !== -1) { + this.setState({ goldRewardInputError: true}) + return; + } else { + this.props.setRewardDialogVisible( false ) + this.props.rewardCode(goldRewardInput) + } + } + onGoldRewardInputChange(event) { + this.setState({ goldRewardInput: event.target.value, goldRewardInputError: false }); + } + + render() { + const { goldRewardDialogOpen } = this.props; + const { goldRewardInputError } = this.state; + const goldRewardInputErrorObj = goldRewardInputError ? {'error': 'error'} : {} + + return ( + + {"奖励设置"} + + + + 请输入奖励的金币数量 + this.onGoldRewardInputChange(e)} /> + { goldRewardInputError ? 奖励金币不能为空或负数 : ''} + + + {/* */} + + + + + + + ); + } +} + +export default RewardDialog; diff --git a/public/react/src/modules/login/LoginDialog.js b/public/react/src/modules/login/LoginDialog.js index c6a5e3076..a5f0529e0 100644 --- a/public/react/src/modules/login/LoginDialog.js +++ b/public/react/src/modules/login/LoginDialog.js @@ -381,6 +381,7 @@ class LoginDialog extends Component { return ( this.handleDialogClose()} >
diff --git a/public/react/src/modules/login/Trialapplication.js b/public/react/src/modules/login/Trialapplication.js index fafd809fd..a6b38fc6a 100644 --- a/public/react/src/modules/login/Trialapplication.js +++ b/public/react/src/modules/login/Trialapplication.js @@ -538,6 +538,7 @@ class Trialapplication extends Component { style={{height: '0', width: '0', border: 'none', display: "none"}}/>
{ diff --git a/public/react/src/modules/page/MainContentContainer.js b/public/react/src/modules/page/MainContentContainer.js index 534f212a6..d24a5cefb 100644 --- a/public/react/src/modules/page/MainContentContainer.js +++ b/public/react/src/modules/page/MainContentContainer.js @@ -1,946 +1,949 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; - -// import getMuiTheme from 'material-ui/styles/getMuiTheme' -import Dialog, { - DialogActions, - DialogContent, - DialogContentText, - DialogTitle, -} from 'material-ui/Dialog'; - -import Button from 'material-ui/Button'; - -import MainContent from './MainContent' -import UpdateDrawer from './component/UpdateDrawer' - -import axios from 'axios' - -import _ from 'lodash' - -const CancelToken = axios.CancelToken; - -let locationPath = window.location.pathname; - -let readingRepoTimes = 0; - -function updateCodeMirror () { - // (TODO 这里的处理方式比较蹩脚,css没搞定)总高度减去页签头高度 - window.$('#games_repository_contents .CodeMirror').height(window.$('#games_repository_contents').height() - 40) - // 为了解决这个问题:一开始进入TPI,代码框的内容显示不全,需要点击一下代码框才会显示全 - $('#games_repository_valuation .CodeMirror .CodeMirror-scroll').scrollTop('2') - // 选择题切换到编程题会出现codemirror没初始化成功的问题,加下refresh - window.editor_CodeMirror && window.editor_CodeMirror.refresh() -} - -// ============================ ============================ ============================ ============================ -// 老版本的评测等待通知代码 -// ============================ ============================ ============================ ============================ -const $ = window.$; -let next_game = null; - function getRandom(min, max){ - var r = Math.random() * (max - min); - var re = Math.round(r + min); - re = Math.max(Math.min(re, max), min); - - return re; - } - - function tipContent(flag, num, mintes, second, first){ - console.log("第几次查询get_waiting_time:" + first); - var random = getRandom(15, 120); - var mintes = parseInt((second - random) / 60); - var html=tip_half(flag, num, mintes, second); - $("#update_game_tip").html(html); - if(first == 1){ - show_tip_content(); - } - } - // 显示右侧评测等待通知 - function show_tip_content(){ - var $tip = $(".tip-panel-animate"); - $(".update_back_main").show(); - $tip.addClass("animate-tip").show(); - setTimeout(function(){$tip.removeClass("animate-tip")}, 700); - } - //隐藏右侧评测等待通知 - function hide_tip_content(){ - var $tip = $(".tip-panel-animate"); - $tip.addClass("animate-tip-hide"); - $(".-task-title").hide(); - setTimeout(function(){ - $tip.hide().removeClass("animate-tip-hide animate-tip"); - check_hide(); - }, 700); - } - window.hide_tip_content = hide_tip_content; - - //判断是否所有的提示信息都已经隐藏 - function check_hide(){ - var dis = true; - var $tipLeft = $(".tip-panel-animate-left"); - for(var i = 0; i < $tipLeft.length; i++){ - if($tipLeft.eq(i).css("display") == "block"){ - dis = false; - return; - } - } - if($(".tip-panel-animate").css("display") == "block"){ - dis = false; - } - if(dis){ - $(".update_back_main").hide(); - $('#info_tip_tab').removeClass('leftnav-active');//去除系统通知的选中状态 - $('#current_task_tab').addClass('leftnav-active');//选中当前任务 - } - } - - /*评测等待通知*/ - function tip_half(flag, num, mintes, second){ - var htmlvalue=""; - htmlvalue+="

评测等待通知

"; - if(flag == 0){ - htmlvalue+="

目前有" + num + "人正在等待评测,还需等待约" + (mintes > 0 ? mintes + "分钟" : second + "秒") + "。您可以先完成“下一关”任务。

"; - }else{ - htmlvalue+="

目前有超过" + num*10 + "人正在等待评测,请您稍后再试。您可以先完成其他关卡

"; - } - - htmlvalue+="
"; - if(flag == 0){ - htmlvalue+="继续等待"; - if (next_game) { - htmlvalue+="下一关"; - } else { - htmlvalue+="已经是最后一关啦" - } - }else{ - htmlvalue+="知道啦"; - if (next_game) { - htmlvalue+="下一关"; - } else { - htmlvalue+="已经是最后一关啦" - } - } - htmlvalue+="
"; - return htmlvalue; - } - - /*更新通知*/ - function tip_half_update(){ - var htmlvalue=""; - - htmlvalue+="

本次更新设计以下几个步骤,预计需要花费几分钟时间,请耐心等待~

"; - htmlvalue+="
    "; - htmlvalue+="
  1. 更新版本库
  2. "; - htmlvalue+="
  3. 同步评测机制
"; - htmlvalue+="
"; - htmlvalue+="知道啦"; - htmlvalue+="
"; - return htmlvalue; - } -// ============================ ============================ ============================ ============================ -// ============================ ============================ ============================ ============================ - -const UPDATED = 0; -const SAVING = 1; -const SAVED = 2; -const SAVE_FAILED = 3; - -class MainContentContainer extends Component { - - constructor(props) { - super(props) - - this.onRepositoryCodeUpdate = this.onRepositoryCodeUpdate.bind(this) - this.onRunCodeTest = this.onRunCodeTest.bind(this) - this.codemirrorDidMount = this.codemirrorDidMount.bind(this) - this.fetchRepositoryCode = this.fetchRepositoryCode.bind(this) - - this.showResetCodeDialog = this.showResetCodeDialog.bind(this) - this.showResetPassedCodeDialog = this.showResetPassedCodeDialog.bind(this) - - - this.oldRepositoryCode = ''; - - this.state = { - repositoryCode: '', - - open: false, // 繁忙等级等提示用Dialog,考虑重构封装到根组件 - gameBuilding: false, // 评测中标志 - codeStatus: SAVED, // 0 已修改 1 保存中 2 已保存 3 保存失败 - - codeLoading: true, // code加载中 - readRepoTimeout: false, // 加载代码轮训超时 - resetCodeDialogOpen: false, // 考虑重构封装到根组件 - resetPassedCodeDialogOpen: false, // 考虑重构封装到根组件 - - isEditablePath: true - } - } - - // ------------------------------------------------ - // static childContextTypes = { - // muiTheme: PropTypes.object - // } - - // getChildContext() { - // return { - // muiTheme: getMuiTheme() - // } - // } - // ------------------------------------------------ - handleClose = () => { - this.setState({open: false}); - }; - - shouldComponentUpdate(nextProps, nextState) { - // _.isEqual(this.props, nextProps) - if (JSON.stringify(nextProps) !== JSON.stringify(this.props) || !_.isEqual(this.state, nextState)) { - return true - } else { - return false - } - } - - componentWillReceiveProps(newProps, nContext) { - // 编程题才需要拿代码 - if (newProps.game && newProps.st === 0) { - if (!this.props || !this.props.game - || ( newProps.game.identifier !== this.props.game.identifier ) ) { - setTimeout(this.fetchRepositoryCode( newProps), 1500); - } else if ( this.props.challenge.pathIndex != newProps.challenge.pathIndex - && newProps.challenge.pathIndex !== -1) { // 切换到只读文件 - // pathIndex切换 - setTimeout(this.fetchRepositoryCode( newProps), 1500); - } - } - if (newProps.myshixun) { - var stageId = newProps.match.params.stageId; - var shixunId = newProps.myshixun.identifier; - // locationPath = `/myshixuns/${shixunId}/stages/${stageId}` - locationPath = `/api/v1/games/${newProps.game.identifier}`; - next_game = newProps.next_game; - } - } - - componentDidUpdate(prevProps, prevState, snapshot) { - const { game } = this.props - - if (game && prevProps.game - && prevProps.game.identifier !== game.identifier) { - // 切换关卡时,停止轮训 - this.oldGameIdentifier = prevProps.game.identifier; - } - - } - - // arg_path 点击文件目录树时,传入的点击节点对应的path - fetchRepositoryCode( props, arg_path, type, isRetry) { - const { challenge, showSnackbar, game, shixun, myshixun, hide_code } = props ? props : this.props; - if (shixun.vnc == true) { - // vnc模式下不需要加载代码 - return true; - } - if ( - // true || - hide_code) { // 隐藏code的实训 - this.setState({ codeLoading: false }); - return; - } - const stageId = game.identifier - let path; - let isEditablePath = false; - if (arg_path) { - path = arg_path; - if (challenge.multiPath) { - challenge.path.forEach(item => { - if (path == item) { - isEditablePath = true; - } - }) - } else if (path == challenge.path) { - isEditablePath = true; - } - } else { - isEditablePath = true; - path = challenge.multiPath ? challenge.path[challenge.pathIndex] : challenge.path; - } - if (readingRepoTimes === 0) { - this.setState({ - readRepoTimeout: false - }) - } - - // http://localhost:3000/api/v1/games/zl6kx8f7vfpo/rep_content - let status = type ? type : 0; // type 1 目录树点击 默认 0: 正常代码加载 - // const fetchRepoCodeUrl = `/api/v1/games/${stageId}/rep_content?path=${path}&shixun_gpid=${shixun.gpid}&status=${status}&retry=${isRetry ? 1 : 0}` - const fetchRepoCodeUrl = `/tasks/${game.identifier}/rep_content.json?path=${path}&status=${status}&retry=${isRetry ? 1 : 0}` - // ?path=${path}&shixun_gpid=${shixun.gpid}&status=${status}&retry=${isRetry ? 1 : 0} - - if (this.state.codeLoading === true && this._cancel) { // 多次请求,则cancel掉前面的请求 - // bug cancel掉第一次请求后,第二次请求也没法发出,先注释掉了 - // this._cancel(); - // this._cancel = null; - } - const that = this; - this.setState({ gameBuilding: false, codeLoading: true }); - axios.get(fetchRepoCodeUrl, { - // path: path - - // withCredentials: true, - // cancelToken: new CancelToken(function executor(c) { - // // An executor function receives a cancel function as a parameter - // that._cancel = c; - // }) - }).then((fetchRepositoryCodeResponse) => { - // 空字符串还是正常切换 - if (fetchRepositoryCodeResponse.data.status == 0) { - readingRepoTimes = readingRepoTimes + 1; - if(readingRepoTimes > 9) { - this.setState({ - readRepoTimeout: true - }) - readingRepoTimes = 0; - showSnackbar(`网络异常,请稍后重试。`); - return - }else{ - if (status === 0) { - // 轮训后点击切换关卡的话,停止上一关的轮训 - if (game.identifier == this.oldGameIdentifier) { - this.oldGameIdentifier = null; - return; - } - setTimeout(() => { - this.fetchRepositoryCode(props,arg_path,type) - }, 1500); - } - } - - }else if (fetchRepositoryCodeResponse.data && fetchRepositoryCodeResponse.data.status === -1) { - this.setState({ codeLoading: false }) - console.error('`获取代码失败!') - showSnackbar(`获取代码失败:${fetchRepositoryCodeResponse.data.message}`) - - }else if (fetchRepositoryCodeResponse.data.status === -3) { - readingRepoTimes = readingRepoTimes + 1; - if(readingRepoTimes > 9) { - this.setState({ - readRepoTimeout: true - }) - readingRepoTimes = 0; - showSnackbar(`网络异常,请稍后重试。`); - return - }else{ - if (status === 0) { - // 轮训后点击切换关卡的话,停止上一关的轮训 - if (game.identifier == this.oldGameIdentifier) { - this.oldGameIdentifier = null; - return; - } - // 重试调用的这个 this.props.fetchRepositoryCode(this.props, null, null, true) - setTimeout(() => { - this.fetchRepositoryCode(props, arg_path, type, 1) - }, 1500); - } - } - } else if (fetchRepositoryCodeResponse.data.status === -4) { - // 异常 直接重试 - this.fetchRepositoryCode(props, null, null, true) - } else{ - this.setState({ isEditablePath, currentPath: path }); - this.oldRepositoryCode = fetchRepositoryCodeResponse.data.content || ''; - this.updateRepositoryCode(this.oldRepositoryCode, updateCodeMirror) - } - - }).catch(error =>{ - console.log(error) - this.setState({ codeLoading: false }); - showSnackbar(`服务端异常,请联系管理员!`); - }) - } - - componentDidMount() { - - - window.$(window.documents).bind("submitChoose",function(){ - - alert("hello world!"); - - }); - - - - } - - doFileUpdateRequestOnCodeMirrorBlur = () => { - var fileUpdatePromise = this.doFileUpdateRequest(true) - if (fileUpdatePromise) { - fileUpdatePromise.then((resData) => { - if (resData.status === -1) { // 保存文件出现异常 - this.setState({ - codeStatus: SAVE_FAILED - }) - return; - } - this.setState({ - codeStatus: SAVED - }) - }).catch((e) => { - console.log(e) - this.setState({ - codeStatus: SAVE_FAILED - }) - }) - } - } - componentWillUnmount() { - this.autoSaveInterval && clearInterval(this.autoSaveInterval); - } - - onEditorBlur = () => { - if (this.autoSaveInterval) { - clearInterval(this.autoSaveInterval); - this.autoSaveInterval = null; - } - this.doFileUpdateRequestOnCodeMirrorBlur() - } - onEditorFocus = () => { - if (this.autoSaveInterval) { - return; - } - this.autoSaveInterval = setInterval( () => { - // code变化时,,每30秒保存到数据库,没变化则不处理 - var autoSave = true - var fileUpdatePromise = this.doFileUpdateRequest(true) - if (fileUpdatePromise) { - fileUpdatePromise.then((resData) => { - // TODO 保存失败的处理 - this.setState({ - codeStatus: SAVED - }) - }).catch((e) => { - console.log(e) - }) - } - }, 30000); - } - - codemirrorDidMount() { - var editor_CodeMirror = window.editor_CodeMirror; - var editor_monaco = window.editor_monaco; - // 失去焦点终止保存事件 - // editor_CodeMirror && editor_CodeMirror.on('blur', this.onEditorBlur); - // editor_monaco && editor_monaco.onDidBlurEditorText(this.onEditorBlur) - - // 每60秒钟保存一次 - editor_CodeMirror && editor_CodeMirror.on('focus', this.onEditorFocus); - editor_monaco && editor_monaco.onDidFocusEditorText(this.onEditorFocus) - - - } - - _refreshHtmlIframe(newCode) { - // TODO 是否是html的code - const isHtmlCode = this.props.challenge.isHtml; - if (isHtmlCode) { - // TODO 参考 _code_actions.html.erb 做link script标签替换 - // var $iframe = window.$('#htmlIframe'); - // $iframe.ready( () => { - // $iframe.contents().find("body").html(newCode || this.state.repositoryCode); - // }); - window.tpi_html_show(newCode) - } - } - - updateRepositoryCode(newCode, callback) { - this.setState({ - codeLoading: false, - repositoryCode: newCode, - }, () => { callback && callback() }); - this._refreshHtmlIframe(newCode) - } - - onRepositoryCodeUpdate(newCode) { - if (this.refreshHtmlTimeout) { - clearTimeout(this.refreshHtmlTimeout) - } - // 每2s刷新一次 - this.setState({ - codeStatus: UPDATED - }) - this.refreshHtmlTimeout = setTimeout(()=>{ - // this.setState({ - // // codeStatus: UPDATED, - // repositoryCode: newCode - // }); - - this._refreshHtmlIframe(newCode) - - this.refreshHtmlTimeout = null; - }, 1500) - - } - - // forTest true 是评测时触发的file_update - doFileUpdateRequest(checkIfCodeChanged, forTest) { - const { codeStatus } = this.state; - if (!forTest && codeStatus !== UPDATED) { // 已修改状态才能保存 - return; - } - // TODO 连着2次请求服务端会报500 - // if (forTest == true) { - // if (this.state.codeStatus != UPDATED) { - - // return; - // } - // } - let codeContent = this.state.repositoryCode; - if (window['editor_CodeMirror']) { - codeContent = window.editor_CodeMirror.getValue(); - } else if (window.editor_monaco) { - codeContent = window.editor_monaco.getValue() - } - if (checkIfCodeChanged === true && this.oldRepositoryCode == codeContent) { - // console.log('code not changed'); - this.setState({ - codeStatus: SAVED - }) - return null; - } - const { challenge, output_sets, onRunCodeTestFinish, myshixun } = this.props - // var url = `${locationPath}/file_update?path=${encodeURIComponent(challenge.path)}` - var url = `/myshixuns/${myshixun.identifier}/update_file.json` - - this.setState({ - codeStatus: SAVING - }) - this.oldRepositoryCode = codeContent; - - let argPath; - if (challenge.pathIndex === -1) { // 当前是只读文件 - argPath = challenge.multiPath === true ? challenge.path[0] : challenge.path - } else { - argPath = challenge.multiPath === true ? challenge.path[challenge.pathIndex] : challenge.path - } - - - - // 跨域请求时会多一个options请求 - // https://github.com/axios/axios/issues/475 - return axios.post(url, { - content: codeContent, - // 评测的时候传1,其它情况不用传,主要是为了区分是用户自己提交还是自动提交 - // type: forTest === true ? 1 : 0, - evaluate: forTest === true ? 1 : 0, - game_id : this.props.game.id, - path: argPath - }, - { - // withCredentials: true - } - - ) - } - - onRunChooseTest() { - const { st, game, onRunChooseTestFinish, showSnackbar } = this.props; - // 获取form表单值 - var value = this.refs.mainContent.refs.chooseQ.getForm().getFieldsValue(); - - var valueArray = []; // map转array - var unSelectOptionIndexArray = [] // 自己做未选提示 - for(var key in value) { - if (!value[key] || (_.isArray(value[key]) && !value[key].join('')[0])) { - unSelectOptionIndexArray.push(key) - break; // 为空处理 - } - // TODO array判断 - valueArray[parseInt(key)] = _.isArray(value[key]) ? value[key].join('') : value[key] ; - } - if (unSelectOptionIndexArray.length) { - var unSelectOptionIndex = unSelectOptionIndexArray[0] - var _unSelectOptionDom = window.$('#games_repository_contents #choice' + unSelectOptionIndex )[0] - _unSelectOptionDom.scrollIntoView() - showSnackbar(`请先给第${parseInt(unSelectOptionIndex)+1}题选择一个答案。`) // , 'top', 'center' - // 有未选择的题 - return; - } - - // TODO 未选提示、props.onRunChooseTest - console.log(unSelectOptionIndexArray) - - console.log('valueArray', valueArray) - - var url = `/api/v1/games/${game.identifier}/choose_build` - this.setState({ - gameBuilding: true, - }) - axios.post(url, { - answer: valueArray - }, - { - withCredentials: true, - // headers: { - // 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', - // } - } - ).then((response) => { - if (response.data.test_sets) { // 评测完成 - - onRunChooseTestFinish(response.data); - this.setState({ - gameBuilding: false, - }) - } - console.log(response) - }).catch((error) => { - console.log(error) - this.setState({ - gameBuilding: false, - }) - }) - } - - setGameBuildFalse() { - this.setState({ - gameBuilding: false - }) - } - onRunCodeTest() { - // tipContent(0, 100, 30, 360, 1) - // return; // for test - - const { st, challenge, output_sets, onRunCodeTestFinish, loading, showDialog, handleGdialogClose, onPathChange } = this.props - const { isEditablePath } = this.state; - if (isEditablePath === false) { - showDialog({ - contentText: '需要先切回可编辑的文件才可评测,确认要现在切换吗?', - callback: () => { - onPathChange(0) - handleGdialogClose(); - } - }) - return; - } - if (loading === true) { - return; // 还在加载中 - } - if (st === 1) { // 选择题 - this.onRunChooseTest(); - return; - } - // ---------------------- 以下是编程题处理 ---------------------- - - console.log('onRunCodeTest onRunCodeTest') - - // http://localhost:3000/myshixuns/so5w6iap97/stages/zl6kx8f7vfpo/file_update?path=src%2Fstep6%2FLinkedList.java - - - // var url = `${locationPath}/file_update?path=${encodeURIComponent(challenge.path)}` - - // var timeOut = parseInt(<%= @myshixun.main_mirror.try(:time_limit) %>); // 超时参数 - - this.setEvaluateMiddleStatusText('') - - this.setState({ - gameBuilding: true - }) - this.doFileUpdateRequest(null, true) - .then((fileUpdateResponse) => { - console.log(fileUpdateResponse); - if (fileUpdateResponse.data.status === -1) { // 保存文件出现异常 - this.setGameBuildFalse() - this.setState({ - codeStatus: SAVE_FAILED - }) - return; - } - this.setState({ - codeStatus: SAVED - }) - this.gameBuild(fileUpdateResponse, 1) - }) - .catch( (fileUpdateError) => { - console.log(fileUpdateError); - this.setGameBuildFalse() - }); - } - setEvaluateMiddleStatusText = (msg) => { - window.$('#evaluateMiddleStatusText').html(msg) - } - // 之前的task_commit方法 - gameBuild(fileUpdateResponse, first) { - const { st, challenge, output_sets, onRunCodeTestFinish, resetTestSetsExpandedArray, showSnackbar, time_limit, game } = this.props - const { resubmit, content_modified, sec_key } = fileUpdateResponse.data; - const timeOut = time_limit; - // http://localhost:3000/myshixuns/so5w6iap97/stages/zl6kx8f7vfpo/game_build?first=1&resubmit=GDBEX741_1993 - // const game_build_url = `${locationPath}/game_build?first=${first}&resubmit=${resubmit}&content_modified=${content_modified}` - const game_build_url = `/tasks/${game.identifier}/game_build.json?first=${first}&resubmit=${resubmit}&content_modified=${content_modified}&sec_key=${sec_key}` - - // var timeOut = parseInt(<%= @myshixun.main_mirror.try(:time_limit) %>); // 超时参数 - - resetTestSetsExpandedArray(); - axios.get(game_build_url, { - // withCredentials: true, - }).then((gameBuildResponse) => { - console.log(gameBuildResponse); - // D:\Code\trustieplus\app\views\games\_code_actions.html.erb - - let { port, ableToCreate, waitNum, waitingTime, had_done } = gameBuildResponse.data; - // http://localhost:3000/myshixuns/so5w6iap97/stages/zl6kx8f7vfpo/game_status?resubmit=GDBEX741_1993&port=50241&time_out=false - - // TODO dead code 往下走 - - // if (ableToCreate == 1) { - - if (gameBuildResponse.data.status == -1) { - return - } - let timeOutFlag = false; - const intervalDuring = 1000; - let requestTimes = 0; - - var gameStatusIntervalId = setInterval(()=>{ - // let game_status_url = `${locationPath}/game_status?port=${port}&resubmit=${resubmit||""}&time_out=${timeOutFlag}` - let game_status_url = `/tasks/${game.identifier}/game_status.json?port=${port}&resubmit=${resubmit||""}&time_out=${timeOutFlag}&sec_key=${sec_key}` - - axios.get(game_status_url, { - // withCredentials: true, - }).then((gameStatusResponse) => { - requestTimes++; - const { status, running_code_message } = gameStatusResponse.data; - - if(!gameStatusResponse.data || !status && status !== 0) { - if(requestTimes >= timeOut - 1){ // 最后一次参数改为true - timeOutFlag = true; - } - if (running_code_message) { - this.setEvaluateMiddleStatusText(running_code_message) - } - return; - } - - if (timeOutFlag === false && (status === 2 || status === 0)) { - // 网络太慢或服务处理较慢的情况下,可能这里会执行第二次 - console.log('clearIntervalclearIntervalclearIntervalclearInterval status2 0') - clearInterval(gameStatusIntervalId); - onRunCodeTestFinish(gameStatusResponse.data) - this.setGameBuildFalse() - timeOutFlag = true; // 用这个处理重复的获取status为2或0的状态 - } - // 超时处理 - if(timeOutFlag === true) { - // clearInterval(gameStatusIntervalId); - console.log('超时处理返回值') - this._refreshHtmlIframe(); - this.setGameBuildFalse() - return; - } - - if(requestTimes >= timeOut - 1){ // 最后一次参数改为true - timeOutFlag = true; - } - - - }).catch((gameStatusError) => { - console.log(gameStatusError); - }) - - if (timeOutFlag === true) { - clearInterval(gameStatusIntervalId); - this.setState({ - open: true, - gameBuilding: false - }) - } - }, intervalDuring) - - window.gameStatusIntervalId = gameStatusIntervalId; - // } else if(ableToCreate === 0) { // 排队等待,重新执行gameBuild - // const mintes = parseInt((waitNum * 120) / 60); // 等待时间 = 等待人数 * (15(秒)---3分钟的随机数); - // tipContent(ableToCreate, waitNum, mintes, waitNum * 180, first); - // setTimeout(()=>{ - // this.gameBuild(fileUpdateResponse, first + 1) - // }, 10000) - // this.setGameBuildFalse() - // } else if(ableToCreate == -1) { // 排队人数过多,建议先玩下一关 - // tipContent(ableToCreate, waitNum, 0, 0, first); - // this.setGameBuildFalse() - // } else { - // showSnackbar("实训云平台繁忙(繁忙等级:94),请稍后刷新并重试") - // this.setGameBuildFalse() - // } - - - }).catch((gameBuildError) => { - console.log(gameBuildError); - this.setGameBuildFalse() - }) - } - handleResetCodeDialogClose() { - this.setState({ resetCodeDialogOpen: false }) - } - showResetCodeDialog() { - this.setState({ resetCodeDialogOpen: true }) - } - doResetCode() { - const { game, challenge, showSnackbar } = this.props; - this.handleResetCodeDialogClose(); - - let path = challenge.path - if (challenge.multiPath) { - path = path[challenge.pathIndex] - } - // const url = `/api/v1/games/${game.identifier}/reset_original_code?path=${path}`; - const url = `/tasks/${game.identifier}/reset_original_code.json?path=${path}`; - this.setState({ - codeLoading: true - }) - axios.get(url, { - // withCredentials: true, - }).then((res) => { - this.handleResetCodeDialogClose(); - if (res.data.status === -1) { - console.error('代码重置失败!'); - this.setState({ - codeLoading: false - }) - showSnackbar(res.data.message || '加载初始的代码失败') - return; - } - // this.setState({ - // codeLoading: false - // }) - this.updateRepositoryCode(res.data.content, updateCodeMirror) - }).catch(error =>{ - this.setState({ - codeLoading: false - }) - }) - } - - handleResetPassedCodeDialogClose() { - this.setState({ resetPassedCodeDialogOpen: false }) - } - showResetPassedCodeDialog() { - this.setState({ resetPassedCodeDialogOpen: true }) - } - doResetPassedCode() { - this.handleResetPassedCodeDialogClose(); - const { game, challenge, showSnackbar } = this.props; - let path = challenge.path - if (challenge.multiPath) { - path = path[challenge.pathIndex] - } - // const url = `/api/v1/games/${game.identifier}/reset_passed_code?path=${path}`; - const url = `/tasks/${game.identifier}/reset_passed_code.json?path=${path}`; - this.setState({ - codeLoading: true - }) - axios.get(url - // , { - // withCredentials: true, - // } - ).then((res) => { - this.handleResetPassedCodeDialogClose(); - if (res.data.status === -1) { - console.error('passed代码重置失败!') - this.setState({ - codeLoading: false - }) - showSnackbar(res.data.message || '加载上次通过的代码失败') - return; - } - // 注意,最好是合并到一个setState, 这里分两次setState的话,触发了2次重新渲染 - // this.setState({ - // codeLoading: false - // }) - this.updateRepositoryCode(res.data.content, updateCodeMirror) - }).catch(error =>{ - this.setState({ - codeLoading: false - }) - }) - } - - render() { - const { challenge, output_sets, time_limit } = this.props; - // TODO 这里直接写死了,game_status在超时时也会返回这句话 - let msg = ( -
- {`代码评测超时!本关限定时间为:${time_limit}s,`}

- 请检查代码是否存在死循环或其他耗时操作 -
) - - return ( - - - {"提示"} - - - { msg } - - - - - - - - this.handleResetCodeDialogClose()} - > - {"提示"} - -
- 你在本文件中修改的内容将丢失

是否确定重新加载初始代码? -
-
- - - - -
- - this.handleResetPassedCodeDialogClose()} - > - {"提示"} - -
- 你在本关中修改的内容将丢失

是否确定重新加载上次通过的代码? -
-
- - - - -
- - - -
- - ); - } -} - -export default MainContentContainer; +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; + +// import getMuiTheme from 'material-ui/styles/getMuiTheme' +import Dialog, { + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, +} from 'material-ui/Dialog'; + +import Button from 'material-ui/Button'; + +import MainContent from './MainContent' +import UpdateDrawer from './component/UpdateDrawer' + +import axios from 'axios' + +import _ from 'lodash' + +const CancelToken = axios.CancelToken; + +let locationPath = window.location.pathname; + +let readingRepoTimes = 0; + +function updateCodeMirror () { + // (TODO 这里的处理方式比较蹩脚,css没搞定)总高度减去页签头高度 + window.$('#games_repository_contents .CodeMirror').height(window.$('#games_repository_contents').height() - 40) + // 为了解决这个问题:一开始进入TPI,代码框的内容显示不全,需要点击一下代码框才会显示全 + $('#games_repository_valuation .CodeMirror .CodeMirror-scroll').scrollTop('2') + // 选择题切换到编程题会出现codemirror没初始化成功的问题,加下refresh + window.editor_CodeMirror && window.editor_CodeMirror.refresh() +} + +// ============================ ============================ ============================ ============================ +// 老版本的评测等待通知代码 +// ============================ ============================ ============================ ============================ +const $ = window.$; +let next_game = null; + function getRandom(min, max){ + var r = Math.random() * (max - min); + var re = Math.round(r + min); + re = Math.max(Math.min(re, max), min); + + return re; + } + + function tipContent(flag, num, mintes, second, first){ + console.log("第几次查询get_waiting_time:" + first); + var random = getRandom(15, 120); + var mintes = parseInt((second - random) / 60); + var html=tip_half(flag, num, mintes, second); + $("#update_game_tip").html(html); + if(first == 1){ + show_tip_content(); + } + } + // 显示右侧评测等待通知 + function show_tip_content(){ + var $tip = $(".tip-panel-animate"); + $(".update_back_main").show(); + $tip.addClass("animate-tip").show(); + setTimeout(function(){$tip.removeClass("animate-tip")}, 700); + } + //隐藏右侧评测等待通知 + function hide_tip_content(){ + var $tip = $(".tip-panel-animate"); + $tip.addClass("animate-tip-hide"); + $(".-task-title").hide(); + setTimeout(function(){ + $tip.hide().removeClass("animate-tip-hide animate-tip"); + check_hide(); + }, 700); + } + window.hide_tip_content = hide_tip_content; + + //判断是否所有的提示信息都已经隐藏 + function check_hide(){ + var dis = true; + var $tipLeft = $(".tip-panel-animate-left"); + for(var i = 0; i < $tipLeft.length; i++){ + if($tipLeft.eq(i).css("display") == "block"){ + dis = false; + return; + } + } + if($(".tip-panel-animate").css("display") == "block"){ + dis = false; + } + if(dis){ + $(".update_back_main").hide(); + $('#info_tip_tab').removeClass('leftnav-active');//去除系统通知的选中状态 + $('#current_task_tab').addClass('leftnav-active');//选中当前任务 + } + } + + /*评测等待通知*/ + function tip_half(flag, num, mintes, second){ + var htmlvalue=""; + htmlvalue+="

评测等待通知

"; + if(flag == 0){ + htmlvalue+="

目前有" + num + "人正在等待评测,还需等待约" + (mintes > 0 ? mintes + "分钟" : second + "秒") + "。您可以先完成“下一关”任务。

"; + }else{ + htmlvalue+="

目前有超过" + num*10 + "人正在等待评测,请您稍后再试。您可以先完成其他关卡

"; + } + + htmlvalue+="
"; + if(flag == 0){ + htmlvalue+="继续等待"; + if (next_game) { + htmlvalue+="下一关"; + } else { + htmlvalue+="已经是最后一关啦" + } + }else{ + htmlvalue+="知道啦"; + if (next_game) { + htmlvalue+="下一关"; + } else { + htmlvalue+="已经是最后一关啦" + } + } + htmlvalue+="
"; + return htmlvalue; + } + + /*更新通知*/ + function tip_half_update(){ + var htmlvalue=""; + + htmlvalue+="

本次更新设计以下几个步骤,预计需要花费几分钟时间,请耐心等待~

"; + htmlvalue+="
    "; + htmlvalue+="
  1. 更新版本库
  2. "; + htmlvalue+="
  3. 同步评测机制
"; + htmlvalue+="
"; + htmlvalue+="知道啦"; + htmlvalue+="
"; + return htmlvalue; + } +// ============================ ============================ ============================ ============================ +// ============================ ============================ ============================ ============================ + +const UPDATED = 0; +const SAVING = 1; +const SAVED = 2; +const SAVE_FAILED = 3; + +class MainContentContainer extends Component { + + constructor(props) { + super(props) + + this.onRepositoryCodeUpdate = this.onRepositoryCodeUpdate.bind(this) + this.onRunCodeTest = this.onRunCodeTest.bind(this) + this.codemirrorDidMount = this.codemirrorDidMount.bind(this) + this.fetchRepositoryCode = this.fetchRepositoryCode.bind(this) + + this.showResetCodeDialog = this.showResetCodeDialog.bind(this) + this.showResetPassedCodeDialog = this.showResetPassedCodeDialog.bind(this) + + + this.oldRepositoryCode = ''; + + this.state = { + repositoryCode: '', + + open: false, // 繁忙等级等提示用Dialog,考虑重构封装到根组件 + gameBuilding: false, // 评测中标志 + codeStatus: SAVED, // 0 已修改 1 保存中 2 已保存 3 保存失败 + + codeLoading: true, // code加载中 + readRepoTimeout: false, // 加载代码轮训超时 + resetCodeDialogOpen: false, // 考虑重构封装到根组件 + resetPassedCodeDialogOpen: false, // 考虑重构封装到根组件 + + isEditablePath: true + } + } + + // ------------------------------------------------ + // static childContextTypes = { + // muiTheme: PropTypes.object + // } + + // getChildContext() { + // return { + // muiTheme: getMuiTheme() + // } + // } + // ------------------------------------------------ + handleClose = () => { + this.setState({open: false}); + }; + + shouldComponentUpdate(nextProps, nextState) { + // _.isEqual(this.props, nextProps) + if (JSON.stringify(nextProps) !== JSON.stringify(this.props) || !_.isEqual(this.state, nextState)) { + return true + } else { + return false + } + } + + componentWillReceiveProps(newProps, nContext) { + // 编程题才需要拿代码 + if (newProps.game && newProps.st === 0) { + if (!this.props || !this.props.game + || ( newProps.game.identifier !== this.props.game.identifier ) ) { + setTimeout(this.fetchRepositoryCode( newProps), 1500); + } else if ( this.props.challenge.pathIndex != newProps.challenge.pathIndex + && newProps.challenge.pathIndex !== -1) { // 切换到只读文件 + // pathIndex切换 + setTimeout(this.fetchRepositoryCode( newProps), 1500); + } + } + if (newProps.myshixun) { + var stageId = newProps.match.params.stageId; + var shixunId = newProps.myshixun.identifier; + // locationPath = `/myshixuns/${shixunId}/stages/${stageId}` + locationPath = `/api/v1/games/${newProps.game.identifier}`; + next_game = newProps.next_game; + } + } + + componentDidUpdate(prevProps, prevState, snapshot) { + const { game } = this.props + + if (game && prevProps.game + && prevProps.game.identifier !== game.identifier) { + // 切换关卡时,停止轮训 + this.oldGameIdentifier = prevProps.game.identifier; + } + + } + + // arg_path 点击文件目录树时,传入的点击节点对应的path + fetchRepositoryCode( props, arg_path, type, isRetry) { + const { challenge, showSnackbar, game, shixun, myshixun, hide_code } = props ? props : this.props; + if (shixun.vnc == true) { + // vnc模式下不需要加载代码 + return true; + } + if ( + // true || + hide_code) { // 隐藏code的实训 + this.setState({ codeLoading: false }); + return; + } + const stageId = game.identifier + let path; + let isEditablePath = false; + if (arg_path) { + path = arg_path; + if (challenge.multiPath) { + challenge.path.forEach(item => { + if (path == item) { + isEditablePath = true; + } + }) + } else if (path == challenge.path) { + isEditablePath = true; + } + } else { + isEditablePath = true; + path = challenge.multiPath ? challenge.path[challenge.pathIndex] : challenge.path; + } + if (readingRepoTimes === 0) { + this.setState({ + readRepoTimeout: false + }) + } + + // http://localhost:3000/api/v1/games/zl6kx8f7vfpo/rep_content + let status = type ? type : 0; // type 1 目录树点击 默认 0: 正常代码加载 + // const fetchRepoCodeUrl = `/api/v1/games/${stageId}/rep_content?path=${path}&shixun_gpid=${shixun.gpid}&status=${status}&retry=${isRetry ? 1 : 0}` + const fetchRepoCodeUrl = `/tasks/${game.identifier}/rep_content.json?path=${path}&status=${status}&retry=${isRetry ? 1 : 0}` + // ?path=${path}&shixun_gpid=${shixun.gpid}&status=${status}&retry=${isRetry ? 1 : 0} + + if (this.state.codeLoading === true && this._cancel) { // 多次请求,则cancel掉前面的请求 + // bug cancel掉第一次请求后,第二次请求也没法发出,先注释掉了 + // this._cancel(); + // this._cancel = null; + } + const that = this; + this.setState({ gameBuilding: false, codeLoading: true }); + axios.get(fetchRepoCodeUrl, { + // path: path + + // withCredentials: true, + // cancelToken: new CancelToken(function executor(c) { + // // An executor function receives a cancel function as a parameter + // that._cancel = c; + // }) + }).then((fetchRepositoryCodeResponse) => { + // 空字符串还是正常切换 + if (fetchRepositoryCodeResponse.data.status == 0) { + readingRepoTimes = readingRepoTimes + 1; + if(readingRepoTimes > 9) { + this.setState({ + readRepoTimeout: true + }) + readingRepoTimes = 0; + showSnackbar(`网络异常,请稍后重试。`); + return + }else{ + if (status === 0) { + // 轮训后点击切换关卡的话,停止上一关的轮训 + if (game.identifier == this.oldGameIdentifier) { + this.oldGameIdentifier = null; + return; + } + setTimeout(() => { + this.fetchRepositoryCode(props,arg_path,type) + }, 1500); + } + } + + }else if (fetchRepositoryCodeResponse.data && fetchRepositoryCodeResponse.data.status === -1) { + this.setState({ codeLoading: false }) + console.error('`获取代码失败!') + showSnackbar(`获取代码失败:${fetchRepositoryCodeResponse.data.message}`) + + }else if (fetchRepositoryCodeResponse.data.status === -3) { + readingRepoTimes = readingRepoTimes + 1; + if(readingRepoTimes > 9) { + this.setState({ + readRepoTimeout: true + }) + readingRepoTimes = 0; + showSnackbar(`网络异常,请稍后重试。`); + return + }else{ + if (status === 0) { + // 轮训后点击切换关卡的话,停止上一关的轮训 + if (game.identifier == this.oldGameIdentifier) { + this.oldGameIdentifier = null; + return; + } + // 重试调用的这个 this.props.fetchRepositoryCode(this.props, null, null, true) + setTimeout(() => { + this.fetchRepositoryCode(props, arg_path, type, 1) + }, 1500); + } + } + } else if (fetchRepositoryCodeResponse.data.status === -4) { + // 异常 直接重试 + this.fetchRepositoryCode(props, null, null, true) + } else{ + this.setState({ isEditablePath, currentPath: path }); + this.oldRepositoryCode = fetchRepositoryCodeResponse.data.content || ''; + this.updateRepositoryCode(this.oldRepositoryCode, updateCodeMirror) + } + + }).catch(error =>{ + console.log(error) + this.setState({ codeLoading: false }); + showSnackbar(`服务端异常,请联系管理员!`); + }) + } + + componentDidMount() { + + + window.$(window.documents).bind("submitChoose",function(){ + + alert("hello world!"); + + }); + + + + } + + doFileUpdateRequestOnCodeMirrorBlur = () => { + var fileUpdatePromise = this.doFileUpdateRequest(true) + if (fileUpdatePromise) { + fileUpdatePromise.then((resData) => { + if (resData.status === -1) { // 保存文件出现异常 + this.setState({ + codeStatus: SAVE_FAILED + }) + return; + } + this.setState({ + codeStatus: SAVED + }) + }).catch((e) => { + console.log(e) + this.setState({ + codeStatus: SAVE_FAILED + }) + }) + } + } + componentWillUnmount() { + this.autoSaveInterval && clearInterval(this.autoSaveInterval); + } + + onEditorBlur = () => { + if (this.autoSaveInterval) { + clearInterval(this.autoSaveInterval); + this.autoSaveInterval = null; + } + this.doFileUpdateRequestOnCodeMirrorBlur() + } + onEditorFocus = () => { + if (this.autoSaveInterval) { + return; + } + this.autoSaveInterval = setInterval( () => { + // code变化时,,每30秒保存到数据库,没变化则不处理 + var autoSave = true + var fileUpdatePromise = this.doFileUpdateRequest(true) + if (fileUpdatePromise) { + fileUpdatePromise.then((resData) => { + // TODO 保存失败的处理 + this.setState({ + codeStatus: SAVED + }) + }).catch((e) => { + console.log(e) + }) + } + }, 30000); + } + + codemirrorDidMount() { + var editor_CodeMirror = window.editor_CodeMirror; + var editor_monaco = window.editor_monaco; + // 失去焦点终止保存事件 + // editor_CodeMirror && editor_CodeMirror.on('blur', this.onEditorBlur); + // editor_monaco && editor_monaco.onDidBlurEditorText(this.onEditorBlur) + + // 每60秒钟保存一次 + editor_CodeMirror && editor_CodeMirror.on('focus', this.onEditorFocus); + editor_monaco && editor_monaco.onDidFocusEditorText(this.onEditorFocus) + + + } + + _refreshHtmlIframe(newCode) { + // TODO 是否是html的code + const isHtmlCode = this.props.challenge.isHtml; + if (isHtmlCode) { + // TODO 参考 _code_actions.html.erb 做link script标签替换 + // var $iframe = window.$('#htmlIframe'); + // $iframe.ready( () => { + // $iframe.contents().find("body").html(newCode || this.state.repositoryCode); + // }); + window.tpi_html_show(newCode) + } + } + + updateRepositoryCode(newCode, callback) { + this.setState({ + codeLoading: false, + repositoryCode: newCode, + }, () => { callback && callback() }); + this._refreshHtmlIframe(newCode) + } + + onRepositoryCodeUpdate(newCode) { + if (this.refreshHtmlTimeout) { + clearTimeout(this.refreshHtmlTimeout) + } + // 每2s刷新一次 + this.setState({ + codeStatus: UPDATED + }) + this.refreshHtmlTimeout = setTimeout(()=>{ + // this.setState({ + // // codeStatus: UPDATED, + // repositoryCode: newCode + // }); + + this._refreshHtmlIframe(newCode) + + this.refreshHtmlTimeout = null; + }, 1500) + + } + + // forTest true 是评测时触发的file_update + doFileUpdateRequest(checkIfCodeChanged, forTest) { + const { codeStatus } = this.state; + if (!forTest && codeStatus !== UPDATED) { // 已修改状态才能保存 + return; + } + // TODO 连着2次请求服务端会报500 + // if (forTest == true) { + // if (this.state.codeStatus != UPDATED) { + + // return; + // } + // } + let codeContent = this.state.repositoryCode; + if (window['editor_CodeMirror']) { + codeContent = window.editor_CodeMirror.getValue(); + } else if (window.editor_monaco) { + codeContent = window.editor_monaco.getValue() + } + if (checkIfCodeChanged === true && this.oldRepositoryCode == codeContent) { + // console.log('code not changed'); + this.setState({ + codeStatus: SAVED + }) + return null; + } + const { challenge, output_sets, onRunCodeTestFinish, myshixun } = this.props + // var url = `${locationPath}/file_update?path=${encodeURIComponent(challenge.path)}` + var url = `/myshixuns/${myshixun.identifier}/update_file.json` + + this.setState({ + codeStatus: SAVING + }) + this.oldRepositoryCode = codeContent; + + let argPath; + if (challenge.pathIndex === -1) { // 当前是只读文件 + argPath = challenge.multiPath === true ? challenge.path[0] : challenge.path + } else { + argPath = challenge.multiPath === true ? challenge.path[challenge.pathIndex] : challenge.path + } + + + + // 跨域请求时会多一个options请求 + // https://github.com/axios/axios/issues/475 + return axios.post(url, { + content: codeContent, + // 评测的时候传1,其它情况不用传,主要是为了区分是用户自己提交还是自动提交 + // type: forTest === true ? 1 : 0, + evaluate: forTest === true ? 1 : 0, + game_id : this.props.game.id, + path: argPath + }, + { + // withCredentials: true + } + + ) + } + + onRunChooseTest() { + const { st, game, onRunChooseTestFinish, showSnackbar } = this.props; + // 获取form表单值 + var value = this.refs.mainContent.refs.chooseQ.getForm().getFieldsValue(); + + var valueArray = []; // map转array + var unSelectOptionIndexArray = [] // 自己做未选提示 + for(var key in value) { + if (!value[key] || (_.isArray(value[key]) && !value[key].join('')[0])) { + unSelectOptionIndexArray.push(key) + break; // 为空处理 + } + // TODO array判断 + valueArray[parseInt(key)] = _.isArray(value[key]) ? value[key].join('') : value[key] ; + } + if (unSelectOptionIndexArray.length) { + var unSelectOptionIndex = unSelectOptionIndexArray[0] + var _unSelectOptionDom = window.$('#games_repository_contents #choice' + unSelectOptionIndex )[0] + _unSelectOptionDom.scrollIntoView() + showSnackbar(`请先给第${parseInt(unSelectOptionIndex)+1}题选择一个答案。`) // , 'top', 'center' + // 有未选择的题 + return; + } + + // TODO 未选提示、props.onRunChooseTest + console.log(unSelectOptionIndexArray) + + console.log('valueArray', valueArray) + + var url = `/api/v1/games/${game.identifier}/choose_build` + this.setState({ + gameBuilding: true, + }) + axios.post(url, { + answer: valueArray + }, + { + withCredentials: true, + // headers: { + // 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', + // } + } + ).then((response) => { + if (response.data.test_sets) { // 评测完成 + + onRunChooseTestFinish(response.data); + this.setState({ + gameBuilding: false, + }) + } + console.log(response) + }).catch((error) => { + console.log(error) + this.setState({ + gameBuilding: false, + }) + }) + } + + setGameBuildFalse() { + this.setState({ + gameBuilding: false + }) + } + onRunCodeTest() { + // tipContent(0, 100, 30, 360, 1) + // return; // for test + + const { st, challenge, output_sets, onRunCodeTestFinish, loading, showDialog, handleGdialogClose, onPathChange } = this.props + const { isEditablePath } = this.state; + if (isEditablePath === false) { + showDialog({ + contentText: '需要先切回可编辑的文件才可评测,确认要现在切换吗?', + callback: () => { + onPathChange(0) + handleGdialogClose(); + } + }) + return; + } + if (loading === true) { + return; // 还在加载中 + } + if (st === 1) { // 选择题 + this.onRunChooseTest(); + return; + } + // ---------------------- 以下是编程题处理 ---------------------- + + console.log('onRunCodeTest onRunCodeTest') + + // http://localhost:3000/myshixuns/so5w6iap97/stages/zl6kx8f7vfpo/file_update?path=src%2Fstep6%2FLinkedList.java + + + // var url = `${locationPath}/file_update?path=${encodeURIComponent(challenge.path)}` + + // var timeOut = parseInt(<%= @myshixun.main_mirror.try(:time_limit) %>); // 超时参数 + + this.setEvaluateMiddleStatusText('') + + this.setState({ + gameBuilding: true + }) + this.doFileUpdateRequest(null, true) + .then((fileUpdateResponse) => { + console.log(fileUpdateResponse); + if (fileUpdateResponse.data.status === -1) { // 保存文件出现异常 + this.setGameBuildFalse() + this.setState({ + codeStatus: SAVE_FAILED + }) + return; + } + this.setState({ + codeStatus: SAVED + }) + this.gameBuild(fileUpdateResponse, 1) + }) + .catch( (fileUpdateError) => { + console.log(fileUpdateError); + this.setGameBuildFalse() + }); + } + setEvaluateMiddleStatusText = (msg) => { + window.$('#evaluateMiddleStatusText').html(msg) + } + // 之前的task_commit方法 + gameBuild(fileUpdateResponse, first) { + const { st, challenge, output_sets, onRunCodeTestFinish, resetTestSetsExpandedArray, showSnackbar, time_limit, game } = this.props + const { resubmit, content_modified, sec_key } = fileUpdateResponse.data; + const timeOut = time_limit; + // http://localhost:3000/myshixuns/so5w6iap97/stages/zl6kx8f7vfpo/game_build?first=1&resubmit=GDBEX741_1993 + // const game_build_url = `${locationPath}/game_build?first=${first}&resubmit=${resubmit}&content_modified=${content_modified}` + const game_build_url = `/tasks/${game.identifier}/game_build.json?first=${first}&resubmit=${resubmit}&content_modified=${content_modified}&sec_key=${sec_key}` + + // var timeOut = parseInt(<%= @myshixun.main_mirror.try(:time_limit) %>); // 超时参数 + + resetTestSetsExpandedArray(); + axios.get(game_build_url, { + // withCredentials: true, + }).then((gameBuildResponse) => { + console.log(gameBuildResponse); + // D:\Code\trustieplus\app\views\games\_code_actions.html.erb + + let { port, ableToCreate, waitNum, waitingTime, had_done } = gameBuildResponse.data; + // http://localhost:3000/myshixuns/so5w6iap97/stages/zl6kx8f7vfpo/game_status?resubmit=GDBEX741_1993&port=50241&time_out=false + + // TODO dead code 往下走 + + // if (ableToCreate == 1) { + + if (gameBuildResponse.data.status == -1) { + return + } + let timeOutFlag = false; + const intervalDuring = 1000; + let requestTimes = 0; + + var gameStatusIntervalId = setInterval(()=>{ + // let game_status_url = `${locationPath}/game_status?port=${port}&resubmit=${resubmit||""}&time_out=${timeOutFlag}` + let game_status_url = `/tasks/${game.identifier}/game_status.json?port=${port}&resubmit=${resubmit||""}&time_out=${timeOutFlag}&sec_key=${sec_key}` + + axios.get(game_status_url, { + // withCredentials: true, + }).then((gameStatusResponse) => { + requestTimes++; + const { status, running_code_message } = gameStatusResponse.data; + + if(!gameStatusResponse.data || !status && status !== 0) { + if(requestTimes >= timeOut - 1){ // 最后一次参数改为true + timeOutFlag = true; + } + if (running_code_message) { + this.setEvaluateMiddleStatusText(running_code_message) + } + return; + } + + if (timeOutFlag === false && (status === 2 || status === 0)) { + // 网络太慢或服务处理较慢的情况下,可能这里会执行第二次 + console.log('clearIntervalclearIntervalclearIntervalclearInterval status2 0') + clearInterval(gameStatusIntervalId); + onRunCodeTestFinish(gameStatusResponse.data) + this.setGameBuildFalse() + timeOutFlag = true; // 用这个处理重复的获取status为2或0的状态 + } + // 超时处理 + if(timeOutFlag === true) { + // clearInterval(gameStatusIntervalId); + console.log('超时处理返回值') + this._refreshHtmlIframe(); + this.setGameBuildFalse() + return; + } + + if(requestTimes >= timeOut - 1){ // 最后一次参数改为true + timeOutFlag = true; + } + + + }).catch((gameStatusError) => { + console.log(gameStatusError); + }) + + if (timeOutFlag === true) { + clearInterval(gameStatusIntervalId); + this.setState({ + open: true, + gameBuilding: false + }) + } + }, intervalDuring) + + window.gameStatusIntervalId = gameStatusIntervalId; + // } else if(ableToCreate === 0) { // 排队等待,重新执行gameBuild + // const mintes = parseInt((waitNum * 120) / 60); // 等待时间 = 等待人数 * (15(秒)---3分钟的随机数); + // tipContent(ableToCreate, waitNum, mintes, waitNum * 180, first); + // setTimeout(()=>{ + // this.gameBuild(fileUpdateResponse, first + 1) + // }, 10000) + // this.setGameBuildFalse() + // } else if(ableToCreate == -1) { // 排队人数过多,建议先玩下一关 + // tipContent(ableToCreate, waitNum, 0, 0, first); + // this.setGameBuildFalse() + // } else { + // showSnackbar("实训云平台繁忙(繁忙等级:94),请稍后刷新并重试") + // this.setGameBuildFalse() + // } + + + }).catch((gameBuildError) => { + console.log(gameBuildError); + this.setGameBuildFalse() + }) + } + handleResetCodeDialogClose() { + this.setState({ resetCodeDialogOpen: false }) + } + showResetCodeDialog() { + this.setState({ resetCodeDialogOpen: true }) + } + doResetCode() { + const { game, challenge, showSnackbar } = this.props; + this.handleResetCodeDialogClose(); + + let path = challenge.path + if (challenge.multiPath) { + path = path[challenge.pathIndex] + } + // const url = `/api/v1/games/${game.identifier}/reset_original_code?path=${path}`; + const url = `/tasks/${game.identifier}/reset_original_code.json?path=${path}`; + this.setState({ + codeLoading: true + }) + axios.get(url, { + // withCredentials: true, + }).then((res) => { + this.handleResetCodeDialogClose(); + if (res.data.status === -1) { + console.error('代码重置失败!'); + this.setState({ + codeLoading: false + }) + showSnackbar(res.data.message || '加载初始的代码失败') + return; + } + // this.setState({ + // codeLoading: false + // }) + this.updateRepositoryCode(res.data.content, updateCodeMirror) + }).catch(error =>{ + this.setState({ + codeLoading: false + }) + }) + } + + handleResetPassedCodeDialogClose() { + this.setState({ resetPassedCodeDialogOpen: false }) + } + showResetPassedCodeDialog() { + this.setState({ resetPassedCodeDialogOpen: true }) + } + doResetPassedCode() { + this.handleResetPassedCodeDialogClose(); + const { game, challenge, showSnackbar } = this.props; + let path = challenge.path + if (challenge.multiPath) { + path = path[challenge.pathIndex] + } + // const url = `/api/v1/games/${game.identifier}/reset_passed_code?path=${path}`; + const url = `/tasks/${game.identifier}/reset_passed_code.json?path=${path}`; + this.setState({ + codeLoading: true + }) + axios.get(url + // , { + // withCredentials: true, + // } + ).then((res) => { + this.handleResetPassedCodeDialogClose(); + if (res.data.status === -1) { + console.error('passed代码重置失败!') + this.setState({ + codeLoading: false + }) + showSnackbar(res.data.message || '加载上次通过的代码失败') + return; + } + // 注意,最好是合并到一个setState, 这里分两次setState的话,触发了2次重新渲染 + // this.setState({ + // codeLoading: false + // }) + this.updateRepositoryCode(res.data.content, updateCodeMirror) + }).catch(error =>{ + this.setState({ + codeLoading: false + }) + }) + } + + render() { + const { challenge, output_sets, time_limit } = this.props; + // TODO 这里直接写死了,game_status在超时时也会返回这句话 + let msg = ( +
+ {`代码评测超时!本关限定时间为:${time_limit}s,`}

+ 请检查代码是否存在死循环或其他耗时操作 +
) + + return ( + + + {"提示"} + + + { msg } + + + + + + + + this.handleResetCodeDialogClose()} + > + {"提示"} + +
+ 你在本文件中修改的内容将丢失

是否确定重新加载初始代码? +
+
+ + + + +
+ + this.handleResetPassedCodeDialogClose()} + > + {"提示"} + +
+ 你在本关中修改的内容将丢失

是否确定重新加载上次通过的代码? +
+
+ + + + +
+ + + +
+ + ); + } +} + +export default MainContentContainer; diff --git a/public/react/src/modules/page/main/LeftView.js b/public/react/src/modules/page/main/LeftView.js index ec3d4665b..9e39a5508 100644 --- a/public/react/src/modules/page/main/LeftView.js +++ b/public/react/src/modules/page/main/LeftView.js @@ -1,359 +1,360 @@ -import React, { Component } from 'react'; - -import ContentLoader from 'react-content-loader' - -import Tooltip from 'material-ui/Tooltip'; -import Dialog, { - DialogActions, - DialogContent, - DialogContentText, - DialogTitle, -} from 'material-ui/Dialog'; -import { CircularProgress } from 'material-ui/Progress'; - -import Button from 'material-ui/Button'; -import IconButton from 'material-ui/IconButton'; - -import CommentContainer from '../../comment/CommentContainer' -import CommentInput from '../../comment/CommentInput' - -import ChooseAnswerView from '../component/ChooseAnswerView' - -import { withStyles } from 'material-ui/styles'; -import { markdownToHTML } from 'educoder' -import AnswerListContainer from './answer/AnswerListContainer' -import './leftView.css' - -import CodeEvaluateMultiLevelAnswerUnlock from './CodeEvaluateMultiLevelAnswerUnlock' -import MUIDialogStyleUtil from '../component/MUIDialogStyleUtil' -// http://danilowoz.com/create-react-content-loader/ -const MyLoader = () => ( - - - - - - - - - - - - - -) - -const styles = MUIDialogStyleUtil.getTwoButtonStyle( - (theme) => { return { - iconButton: { - margin: theme.spacing.unit, - // background: '#05101A', - color: '#4CACFF', - top: '-7px', - width: '36px', - height: '36px', - } - } - } -) - -// const styles = theme => (); - -class LeftView extends Component { - - goToCertification() { - window.open('/account/professional_certification', '_blank'); - } -// /shixuns/mnf6b7z3/shixun_discuss?challenge_id=88 - render() { - let { challenge, shixun, tabIndex, tabIndexChange, loading, discusses_count - , dialogOpen, handleDialogClose, handleDialogReadAnswer, gameAnswer, loadingComments, st, user, - classes, onDrawerButtonClick, lockedAnswers, unlockedAnswers, isMultiLevelAnswer } = this.props - - let propaedeutics = shixun ? shixun.propaedeutics : null; - let _hasAnswer = challenge.hasAnswer // st === 1 || - // const actions = [ - // : ''} - */ - - return ( - - - {"提示"} - - - { contentText } - - - {/* http://localhost:3000/account/professional_certification */} - - - - {/* variant={ is_teacher ? "flat" : "raised"} */} - - - - - -
- -
- - {/*新界面关卡名称显示、关卡金币显示*/} -
- - { loading ? "" : - - - - - - -

第{challenge.position}关:{challenge.subject}

- {challenge.score} -
- } -
- - -
-
- {/*过关任务*/} -
-
-
- { loading ? -
- -
: "" - } - -
-
-
-
-
- {/*背景知识*/} -
-
-
-
- -
-
-
-
- {/* 参考答案*/} -
-
-
- {/* 只读markdown的写法 markdownToHTML 这个接口生成的markdown没有setMarkdown接口 - */} - - - { (!unlockedAnswers || unlockedAnswers.length === 0) && (!lockedAnswers || lockedAnswers.length === 0) && -
-
- } - - - -
- { unlockedAnswers && unlockedAnswers.map((item, index) => { - return
-
-
级别{index + 1}:
-
{item.name}
-
已解锁
-
-
-
-
- })} - - { lockedAnswers && lockedAnswers.map((item, index) => { - return
-
-
级别{index + 1 + (unlockedAnswers ? unlockedAnswers.length : 0)}:
-
{item.name}
-
{ this.props.showUnlockAnswerDialog(item) } } - style={{ color: '#4CACFF', cursor: 'pointer' }} - >解锁
-
-
- })} - -
- - - { st === 1 ? -
- -
- : '' - } -
-
-
- {/* TA人解答*/} -
-
-
- -
-
-
-
- { loadingComments ? - : - - } -
-
- -
- {/*说点什么 */} - -
-
- -
-
- ); - } -} -// -// {/**/} -export default withStyles(styles)( LeftView ); +import React, { Component } from 'react'; + +import ContentLoader from 'react-content-loader' + +import Tooltip from 'material-ui/Tooltip'; +import Dialog, { + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, +} from 'material-ui/Dialog'; +import { CircularProgress } from 'material-ui/Progress'; + +import Button from 'material-ui/Button'; +import IconButton from 'material-ui/IconButton'; + +import CommentContainer from '../../comment/CommentContainer' +import CommentInput from '../../comment/CommentInput' + +import ChooseAnswerView from '../component/ChooseAnswerView' + +import { withStyles } from 'material-ui/styles'; +import { markdownToHTML } from 'educoder' +import AnswerListContainer from './answer/AnswerListContainer' +import './leftView.css' + +import CodeEvaluateMultiLevelAnswerUnlock from './CodeEvaluateMultiLevelAnswerUnlock' +import MUIDialogStyleUtil from '../component/MUIDialogStyleUtil' +// http://danilowoz.com/create-react-content-loader/ +const MyLoader = () => ( + + + + + + + + + + + + + +) + +const styles = MUIDialogStyleUtil.getTwoButtonStyle( + (theme) => { return { + iconButton: { + margin: theme.spacing.unit, + // background: '#05101A', + color: '#4CACFF', + top: '-7px', + width: '36px', + height: '36px', + } + } + } +) + +// const styles = theme => (); + +class LeftView extends Component { + + goToCertification() { + window.open('/account/professional_certification', '_blank'); + } +// /shixuns/mnf6b7z3/shixun_discuss?challenge_id=88 + render() { + let { challenge, shixun, tabIndex, tabIndexChange, loading, discusses_count + , dialogOpen, handleDialogClose, handleDialogReadAnswer, gameAnswer, loadingComments, st, user, + classes, onDrawerButtonClick, lockedAnswers, unlockedAnswers, isMultiLevelAnswer } = this.props + + let propaedeutics = shixun ? shixun.propaedeutics : null; + let _hasAnswer = challenge.hasAnswer // st === 1 || + // const actions = [ + // : ''} + */ + + return ( + + + {"提示"} + + + { contentText } + + + {/* http://localhost:3000/account/professional_certification */} + + + + {/* variant={ is_teacher ? "flat" : "raised"} */} + + + + + +
+ +
+ + {/*新界面关卡名称显示、关卡金币显示*/} +
+ + { loading ? "" : + + + + + + +

第{challenge.position}关:{challenge.subject}

+ {challenge.score} +
+ } +
+ + +
+
+ {/*过关任务*/} +
+
+
+ { loading ? +
+ +
: "" + } + +
+
+
+
+
+ {/*背景知识*/} +
+
+
+
+ +
+
+
+
+ {/* 参考答案*/} +
+
+
+ {/* 只读markdown的写法 markdownToHTML 这个接口生成的markdown没有setMarkdown接口 + */} + + + { (!unlockedAnswers || unlockedAnswers.length === 0) && (!lockedAnswers || lockedAnswers.length === 0) && +
+
+ } + + + +
+ { unlockedAnswers && unlockedAnswers.map((item, index) => { + return
+
+
级别{index + 1}:
+
{item.name}
+
已解锁
+
+
+
+
+ })} + + { lockedAnswers && lockedAnswers.map((item, index) => { + return
+
+
级别{index + 1 + (unlockedAnswers ? unlockedAnswers.length : 0)}:
+
{item.name}
+
{ this.props.showUnlockAnswerDialog(item) } } + style={{ color: '#4CACFF', cursor: 'pointer' }} + >解锁
+
+
+ })} + +
+ + + { st === 1 ? +
+ +
+ : '' + } +
+
+
+ {/* TA人解答*/} +
+
+
+ +
+
+
+
+ { loadingComments ? + : + + } +
+
+ +
+ {/*说点什么 */} + +
+
+ +
+
+ ); + } +} +// +// {/**/} +export default withStyles(styles)( LeftView );