|
|
|
@ -9,6 +9,7 @@ |
|
|
|
|
<div v-if="isSubmit" |
|
|
|
|
class="code-mask"></div> |
|
|
|
|
<div class="btns"> |
|
|
|
|
<template v-if="!pid"> |
|
|
|
|
<span class="el-icon-delete del" |
|
|
|
|
@click="clearCode"></span> |
|
|
|
|
<el-button v-if="isPrac" |
|
|
|
@ -23,6 +24,16 @@ |
|
|
|
|
type="primary" |
|
|
|
|
@click="runCode(false)" |
|
|
|
|
:disabled="runEnable">运行</el-button> |
|
|
|
|
</template> |
|
|
|
|
<el-popconfirm v-else |
|
|
|
|
title="确定终止运行本次代码?" |
|
|
|
|
@confirm="stopRunCode"> |
|
|
|
|
<el-button slot="reference" |
|
|
|
|
class="run" |
|
|
|
|
type="danger" |
|
|
|
|
:disabled="runEnable">终止运行</el-button> |
|
|
|
|
</el-popconfirm> |
|
|
|
|
|
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
<div class="line"></div> |
|
|
|
@ -420,6 +431,10 @@ export default { |
|
|
|
|
curRow: {}, |
|
|
|
|
curCode: '', |
|
|
|
|
curPhotoUrl: [], |
|
|
|
|
runCodeType: '', |
|
|
|
|
pid: '', |
|
|
|
|
cancelRunTimer: null, |
|
|
|
|
runCodeConfirm: null, |
|
|
|
|
}; |
|
|
|
|
}, |
|
|
|
|
components: { |
|
|
|
@ -445,9 +460,19 @@ export default { |
|
|
|
|
|
|
|
|
|
if (!this.assessmentId && !this.competitionId) this.isPrac = true |
|
|
|
|
//兄弟组件传值 |
|
|
|
|
newmain.$on("isSubmit", isSubmit => { |
|
|
|
|
newmain.$on('isSubmit', isSubmit => { |
|
|
|
|
this.isSubmit = isSubmit |
|
|
|
|
}) |
|
|
|
|
newmain.$on('runCode', data => { |
|
|
|
|
this.handleRunCode(data) |
|
|
|
|
}) |
|
|
|
|
newmain.$on('setPid', pid => { |
|
|
|
|
if (this.runCodeConfirm) this.runCodeConfirm.close() |
|
|
|
|
this.pid = pid |
|
|
|
|
}) |
|
|
|
|
newmain.$on('cancelRunTimer', () => { |
|
|
|
|
clearTimeout(this.cancelRunTimer) |
|
|
|
|
}) |
|
|
|
|
this.dragSide() |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
@ -556,6 +581,44 @@ export default { |
|
|
|
|
}) |
|
|
|
|
}).catch(res => { }) |
|
|
|
|
}, |
|
|
|
|
handleRunCodeCb (fn) { |
|
|
|
|
newmain.$emit('changeRunStatus', '') |
|
|
|
|
this.runCodeType = fn |
|
|
|
|
clearTimeout(this.cancelRunTimer) |
|
|
|
|
// this.cancelRunTimer = setTimeout(() => { |
|
|
|
|
// this.runCodeConfirm = this.$confirm('当前代码已运行超过5秒钟,是否终止运行?', '提示', { |
|
|
|
|
// type: 'warning', |
|
|
|
|
// closeOnClickModal: false, |
|
|
|
|
// showClose: false |
|
|
|
|
// }).then(async () => { |
|
|
|
|
// if (this.pid) await this.$post(`${this.api.endRunningProcess}?pid=${this.pid}`) |
|
|
|
|
// if (this.loadIns) this.loadIns.close() |
|
|
|
|
// }).catch(() => { }) |
|
|
|
|
// }, 5000) |
|
|
|
|
}, |
|
|
|
|
// 终止运行 |
|
|
|
|
async stopRunCode () { |
|
|
|
|
if (this.pid) await this.$post(`${this.api.endRunningProcess}?pid=${this.pid}`) |
|
|
|
|
this.pid = '' |
|
|
|
|
if (this.loadIns) this.loadIns.close() |
|
|
|
|
}, |
|
|
|
|
inputRunCode (data) { |
|
|
|
|
const result = data.runResult |
|
|
|
|
if (result.includes('File ')) { |
|
|
|
|
this.runResult = result |
|
|
|
|
this.errLine = this.getErrLine(result) |
|
|
|
|
this.isError = data.retResult |
|
|
|
|
} else if (result.includes('validing:')) { |
|
|
|
|
this.isError = 1 |
|
|
|
|
this.confirmInput(result) |
|
|
|
|
} else if (data.retResult) { |
|
|
|
|
this.isError = 1 |
|
|
|
|
this.runResult += result |
|
|
|
|
} |
|
|
|
|
this.$emit('update:codeId', data.codeId) // 更新coddeId |
|
|
|
|
this.$emit('update:answer', this.runResult) // 更新运行结果 |
|
|
|
|
this.$emit('update:retResult', data.retResult) // 更新返回结果 |
|
|
|
|
}, |
|
|
|
|
/** |
|
|
|
|
* python代码里如果有input函数的话,是做了单独的处理的,原理是先把所有input函数都替换成exit函数,再在exit函数里加上特定标识,再通过接口传给后端去执行 |
|
|
|
|
* 因为exit函数是跟input有类似的效果,就是都会产生阻塞,所以python引擎一旦遇到exit,进程就会被停止,然后返回exit函数里面的值,而这个值,就是上面说的特定标识加上原本这个input函数里的值 |
|
|
|
@ -583,6 +646,7 @@ export default { |
|
|
|
|
this.requestList.map(n => n('interrupt')) |
|
|
|
|
}, 1000) |
|
|
|
|
|
|
|
|
|
this.handleRunCodeCb('inputRunCode') |
|
|
|
|
axios.post(config.host + this.api.runPythonCode, { |
|
|
|
|
code: this.sourceCode, |
|
|
|
|
bcId: this.judgmentId, |
|
|
|
@ -596,37 +660,86 @@ export default { |
|
|
|
|
cancelToken: new CANCEL_TOKEN(c => { //强行中断请求要用到的,记录请求信息 |
|
|
|
|
this.requestList.push(c) |
|
|
|
|
}) |
|
|
|
|
}).then(response => { |
|
|
|
|
let res = response.data |
|
|
|
|
const data = res.code |
|
|
|
|
}).then(res => { |
|
|
|
|
this.$emit('update:finalCode', this.sourceCode) |
|
|
|
|
}).catch(e => { |
|
|
|
|
if (e && e.message == 'interrupt') { |
|
|
|
|
this.runCode(true) |
|
|
|
|
this.requestList = [] |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
}).catch(err => { }) |
|
|
|
|
}, |
|
|
|
|
// 获取第几行代码有错误 |
|
|
|
|
getErrLine (result) { |
|
|
|
|
return parseInt(result.substring(result.indexOf('line') + 4, result.length)) || 1 |
|
|
|
|
}, |
|
|
|
|
handleRunCode (data) { |
|
|
|
|
if (this.runCodeType && this[this.runCodeType]) this[this.runCodeType](data) |
|
|
|
|
}, |
|
|
|
|
runCodeCb1 (res) { |
|
|
|
|
const result = data.runResult |
|
|
|
|
if (result.includes('File ')) { |
|
|
|
|
if (isWhile) { |
|
|
|
|
this.runResult += result |
|
|
|
|
} else { |
|
|
|
|
this.runResult = result |
|
|
|
|
} |
|
|
|
|
this.errLine = this.getErrLine(result) |
|
|
|
|
this.isError = data.retResult |
|
|
|
|
} else if (result.includes('validing:')) { |
|
|
|
|
this.isError = 1 |
|
|
|
|
this.confirmInput(result) |
|
|
|
|
} else if (data.retResult) { |
|
|
|
|
this.isError = 1 |
|
|
|
|
this.runResult += result |
|
|
|
|
} |
|
|
|
|
this.$emit('update:finalCode', this.sourceCode) |
|
|
|
|
this.$emit('update:codeId', res.codeId) // 更新coddeId |
|
|
|
|
this.$emit('update:answer', this.runResult) // 更新运行结果 |
|
|
|
|
this.$emit('update:retResult', data.retResult) // 更新返回结果 |
|
|
|
|
}).catch(e => { |
|
|
|
|
if (e && e.message == 'interrupt') { |
|
|
|
|
this.runCode(true) |
|
|
|
|
this.requestList = [] |
|
|
|
|
}, |
|
|
|
|
runCodeCb2 (data) { |
|
|
|
|
// 正常返回结果 |
|
|
|
|
if (data) { |
|
|
|
|
const photo = data.photoUrl |
|
|
|
|
const result = data.runResult || '' |
|
|
|
|
this.$emit('cache') // 每次运行代码都要把代码传给后端做缓存 |
|
|
|
|
if (this.loadIns) this.loadIns.close() |
|
|
|
|
this.picSrcList = [] |
|
|
|
|
this.$emit('update:photoUrl', '') |
|
|
|
|
if (photo) { |
|
|
|
|
this.picSrcList = photo.split(',') |
|
|
|
|
this.$emit('update:photoUrl', photo) |
|
|
|
|
} |
|
|
|
|
this.$emit('update:codeId', data.codeId) // 更新coddeId |
|
|
|
|
this.$emit('update:answer', result) // 更新运行结果 |
|
|
|
|
this.$emit('update:retResult', data.retResult) // 更新返回结果 |
|
|
|
|
let imgList = '' |
|
|
|
|
let firtImg = '' |
|
|
|
|
try { |
|
|
|
|
imgList = eval(result) |
|
|
|
|
} catch (error) { } |
|
|
|
|
if (imgList && imgList.length) firtImg = imgList[0] |
|
|
|
|
// 如果是下载图片的代码,则要显示图片和运行结果,不用显示对错,换成显示下载图片 |
|
|
|
|
if (photo) { |
|
|
|
|
this.isError = data.retResult // 对错隐藏 |
|
|
|
|
const text = result.replace(photo, '') // 结果里包含了图片路径,所以要把图片路径给去掉 |
|
|
|
|
this.runResult = text |
|
|
|
|
this.picSrcList = photo.split(',') |
|
|
|
|
this.errLine = this.getErrLine(result) |
|
|
|
|
} else if (imgList instanceof Array && imgList.length && typeof firtImg === 'string' && (firtImg.includes('.jpg') || firtImg.includes('.png') || firtImg.includes('img'))) { |
|
|
|
|
/** |
|
|
|
|
* 这段是为要下载图片的项目案例写的,后端会返回图片名称的数组,前端负责循环这个数组,然后下载下来 |
|
|
|
|
*/ |
|
|
|
|
imgList.map((n, i) => { |
|
|
|
|
util.downloadFile(`${i + 1}.jpg`, n) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
}).catch(err => { }) |
|
|
|
|
}, |
|
|
|
|
// 获取第几行代码有错误 |
|
|
|
|
getErrLine (result) { |
|
|
|
|
return parseInt(result.substring(result.indexOf('line') + 4, result.length)) || 1 |
|
|
|
|
this.isError = 0 |
|
|
|
|
this.runResult = '下载完成' |
|
|
|
|
} else { |
|
|
|
|
this.isError = data.retResult |
|
|
|
|
this.runResult = result |
|
|
|
|
this.errLine = this.getErrLine(result) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
// 运行代码 |
|
|
|
|
runCode (isWhile) { // isWhile为true表示代码里有while循环,右边的运行结果需要拼接展示,而不是直接覆盖 |
|
|
|
@ -657,6 +770,7 @@ export default { |
|
|
|
|
this.sourceCode = code |
|
|
|
|
|
|
|
|
|
if (!isWhile) this.runResult = '' |
|
|
|
|
this.handleRunCodeCb('runCodeCb1') |
|
|
|
|
this.$post(this.api.runPythonCode, { |
|
|
|
|
code, |
|
|
|
|
bcId, |
|
|
|
@ -664,37 +778,23 @@ export default { |
|
|
|
|
projectId, |
|
|
|
|
type: 0 |
|
|
|
|
}).then(res => { |
|
|
|
|
const data = res.code |
|
|
|
|
const result = data.runResult |
|
|
|
|
if (result.includes('File ')) { |
|
|
|
|
if (isWhile) { |
|
|
|
|
this.runResult += result |
|
|
|
|
} else { |
|
|
|
|
this.runResult = result |
|
|
|
|
} |
|
|
|
|
this.errLine = this.getErrLine(result) |
|
|
|
|
this.isError = data.retResult |
|
|
|
|
} else if (result.includes('validing:')) { |
|
|
|
|
this.isError = 1 |
|
|
|
|
this.confirmInput(result) |
|
|
|
|
} |
|
|
|
|
this.$emit('update:finalCode', code) |
|
|
|
|
this.$emit('update:codeId', res.codeId) // 更新coddeId |
|
|
|
|
this.$emit('update:answer', this.runResult) // 更新运行结果 |
|
|
|
|
this.$emit('update:retResult', data.retResult) // 更新返回结果 |
|
|
|
|
}).catch(err => { |
|
|
|
|
this.loadIns.close() |
|
|
|
|
}) |
|
|
|
|
} else { |
|
|
|
|
this.loadIns = Loading.service({ |
|
|
|
|
text: '代码运行中请稍等几分钟', |
|
|
|
|
background: 'transparent' |
|
|
|
|
text: '代码运行中,请稍候', |
|
|
|
|
background: 'transparent', |
|
|
|
|
customClass: 'code-load', |
|
|
|
|
}) |
|
|
|
|
// 如果代码有savefig(python里的保存图片的方法),则要把savefig里的图片名字加上个时间戳,防止后台保存图片到服务器的时候图片名字重复导致覆盖 |
|
|
|
|
code = code.replace(/\.savefig\(([\u4e00-\u9fa5\w]*?['"])/mg, str => { |
|
|
|
|
return str + Date.now() |
|
|
|
|
}) |
|
|
|
|
// 把代码传给后端,在后端运行Python代码 |
|
|
|
|
|
|
|
|
|
this.handleRunCodeCb('runCodeCb2') |
|
|
|
|
this.$post(this.api.runPythonCode, { |
|
|
|
|
code, |
|
|
|
|
bcId, |
|
|
|
@ -702,55 +802,7 @@ export default { |
|
|
|
|
projectId, |
|
|
|
|
type: 0 |
|
|
|
|
}).then(res => { |
|
|
|
|
// 正常返回结果 |
|
|
|
|
if (res.code) { |
|
|
|
|
const data = res.code |
|
|
|
|
const photo = data.photoUrl |
|
|
|
|
const result = data.runResult || '' |
|
|
|
|
this.$emit('cache') // 每次运行代码都要把代码传给后端做缓存 |
|
|
|
|
this.loadIns.close() |
|
|
|
|
this.picSrcList = [] |
|
|
|
|
this.$emit('update:photoUrl', '') |
|
|
|
|
if (photo) { |
|
|
|
|
this.picSrcList = photo.split(',') |
|
|
|
|
this.$emit('update:photoUrl', photo) |
|
|
|
|
} |
|
|
|
|
this.$emit('update:finalCode', code) |
|
|
|
|
this.$emit('update:codeId', res.codeId) // 更新coddeId |
|
|
|
|
this.$emit('update:answer', result) // 更新运行结果 |
|
|
|
|
this.$emit('update:retResult', data.retResult) // 更新返回结果 |
|
|
|
|
let imgList = '' |
|
|
|
|
let firtImg = '' |
|
|
|
|
try { |
|
|
|
|
imgList = eval(result) |
|
|
|
|
} catch (error) { } |
|
|
|
|
if (imgList && imgList.length) firtImg = imgList[0] |
|
|
|
|
// 如果是下载图片的代码,则要显示图片和运行结果,不用显示对错,换成显示下载图片 |
|
|
|
|
if (photo) { |
|
|
|
|
this.isError = data.retResult // 对错隐藏 |
|
|
|
|
const text = result.replace(photo, '') // 结果里包含了图片路径,所以要把图片路径给去掉 |
|
|
|
|
this.runResult = text |
|
|
|
|
this.picSrcList = photo.split(',') |
|
|
|
|
this.errLine = this.getErrLine(result) |
|
|
|
|
} else if (imgList instanceof Array && imgList.length && typeof firtImg === 'string' && (firtImg.includes('.jpg') || firtImg.includes('.png') || firtImg.includes('img'))) { |
|
|
|
|
/** |
|
|
|
|
* 这段是为要下载图片的项目案例写的,后端会返回图片名称的数组,前端负责循环这个数组,然后下载下来 |
|
|
|
|
*/ |
|
|
|
|
imgList.map((n, i) => { |
|
|
|
|
util.downloadFile(`${i + 1}.jpg`, n) |
|
|
|
|
}) |
|
|
|
|
this.isError = 0 |
|
|
|
|
this.runResult = '下载完成' |
|
|
|
|
} else { |
|
|
|
|
this.isError = data.retResult |
|
|
|
|
this.runResult = result |
|
|
|
|
this.errLine = this.getErrLine(result) |
|
|
|
|
} |
|
|
|
|
} else if (res.data && res.data.current) { |
|
|
|
|
// 进入运行队列 |
|
|
|
|
this.$message.success(`当前队列有${res.data.current - 1}人在排队,请稍等!`) |
|
|
|
|
this.loadIns.close() |
|
|
|
|
} |
|
|
|
|
}).catch(res => { |
|
|
|
|
this.isError = false |
|
|
|
|
this.runResult = '' |
|
|
|
|