parent
ddcf41931f
commit
2cf0f0674c
9 changed files with 566 additions and 419 deletions
After Width: | Height: | Size: 796 B |
@ -0,0 +1,402 @@ |
||||
<template> |
||||
<div class="wrap"> |
||||
<div class="left"> |
||||
<codemirror |
||||
v-model="codeVal" |
||||
:options="cmOption" |
||||
class="code-mirror" |
||||
@ready="ready" |
||||
ref="codemirror" |
||||
></codemirror> |
||||
<el-button |
||||
class="run btn" |
||||
type="primary" |
||||
@click="runCode" |
||||
>运行</el-button> |
||||
</div> |
||||
<div class="code-right answer"> |
||||
<p class="text-wrapper">{{ runResult }}</p> |
||||
<div class="pic-wrap" v-if="picSrcList.length"> |
||||
<div class="pic-item" v-for="(img, i) in picSrcList" :key="i"> |
||||
<el-image |
||||
class="pic" |
||||
:src="img" |
||||
:preview-src-list="picSrcList"> |
||||
</el-image> |
||||
<el-button class="download-btn btn" type="primary" size="mini" @click="downloadPic(i)">下载图片</el-button> |
||||
<a :ref="'picLink' + i" style="display: none;" download="运行结果.png" :href="img">下载图片</a> |
||||
</div> |
||||
</div> |
||||
<div class="result-right t-color" v-show="isError"> |
||||
<!-- <img :src="require(`@/assets/images/system/${systemId}/yes.png`)" alt />运行成功 --> |
||||
</div> |
||||
<div class="result-wrong" v-show="isError === 0"> |
||||
<img src="@/assets/img/error.png" alt /> |
||||
第{{errLine}}行出现错误 |
||||
<el-button class="tips-btn" @click="getTips" v-show="showTips">提示</el-button> |
||||
<el-dialog title="答案提示" center :close-on-click-modal="false" :visible.sync="tipsVisible"> |
||||
<el-tabs> |
||||
<el-tab-pane label="参考答案"> |
||||
<div class="answer-wrap" v-html="answer"></div> |
||||
</el-tab-pane> |
||||
</el-tabs> |
||||
</el-dialog> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { codemirror } from "vue-codemirror"; |
||||
import "codemirror/theme/ambiance.css"; // 这里引入的是主题样式 |
||||
import "codemirror/mode/javascript/javascript.js"; |
||||
import "codemirror/mode/python/python.js"; |
||||
import "codemirror/lib/codemirror.css"; |
||||
// require active-line.js |
||||
import "codemirror/addon/selection/active-line.js"; |
||||
// styleSelectedText |
||||
import "codemirror/addon/selection/mark-selection.js"; |
||||
// hint |
||||
import "codemirror/addon/hint/show-hint.js"; |
||||
import "codemirror/addon/hint/sql-hint.js"; |
||||
import "codemirror/addon/hint/show-hint.css"; |
||||
import "codemirror/addon/hint/javascript-hint.js"; |
||||
// highlightSelectionMatches |
||||
import "codemirror/addon/scroll/annotatescrollbar.js"; |
||||
import "codemirror/addon/search/matchesonscrollbar.js"; |
||||
import "codemirror/addon/search/match-highlighter.js"; |
||||
// keyMap |
||||
import "codemirror/mode/clike/clike.js"; |
||||
import "codemirror/mode/sql/sql.js"; |
||||
import "codemirror/addon/edit/matchbrackets.js"; |
||||
import "codemirror/addon/comment/comment.js"; |
||||
import "codemirror/addon/dialog/dialog.js"; |
||||
import "codemirror/addon/dialog/dialog.css"; |
||||
import "codemirror/addon/search/searchcursor.js"; |
||||
import "codemirror/addon/search/search.js"; |
||||
import "codemirror/keymap/sublime.js"; |
||||
// foldGutter |
||||
import "codemirror/addon/fold/foldgutter.css"; |
||||
import "codemirror/addon/fold/brace-fold.js"; |
||||
import "codemirror/addon/fold/comment-fold.js"; |
||||
import "codemirror/addon/fold/foldcode.js"; |
||||
import "codemirror/addon/fold/foldgutter.js"; |
||||
import "codemirror/addon/fold/indent-fold.js"; |
||||
import "codemirror/addon/fold/markdown-fold.js"; |
||||
import "codemirror/addon/fold/xml-fold.js"; |
||||
// 编辑的主题文件 |
||||
import "codemirror/theme/monokai.css"; |
||||
import "codemirror/theme/base16-light.css"; |
||||
import { Loading } from 'element-ui'; |
||||
export default { |
||||
props: ['code', 'codeId', 'projectId', 'retResult'], |
||||
data() { |
||||
return { |
||||
showTips: false, // 显示隐藏提示按钮 |
||||
answer: '', // 正确答案 |
||||
codeVal: this.code, |
||||
runResult: '', // 运行结果 |
||||
isError: false, // 运行正确与否 |
||||
errLine: '', // 错误代码的位置 |
||||
tipsVisible: false, // 答案提示弹框显示标识 |
||||
picSrcList: [], |
||||
loadIns: null, // loading实例 |
||||
cmOption: { |
||||
scrollbarStyle: "native", |
||||
tabSize: 2, // tab |
||||
styleActiveLine: true, // 高亮选中行 |
||||
lineNumbers: true, // 显示行号 |
||||
styleSelectedText: true, |
||||
line: true, |
||||
foldGutter: true, // 块槽 |
||||
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], |
||||
highlightSelectionMatches: { showToken: /\w/, annotateScrollbar: true }, // 可以启用该选项来突出显示当前选中的内容的所有实例 |
||||
mode: 'python', |
||||
lineWrapping: true, //代码折叠 |
||||
autoCloseTags: true,// 自动闭合标签 |
||||
autoCloseBrackets: true,// 自动闭合括号 |
||||
// hint.js options |
||||
hintOptions: { |
||||
// 当匹配只有一项的时候是否自动补全 |
||||
completeSingle: false |
||||
}, |
||||
// 快捷键 可提供三种模式 sublime、emacs、vim |
||||
keyMap: "sublime", |
||||
matchBrackets: true, |
||||
showCursorWhenSelecting: true, |
||||
theme: "monokai" // 主题 |
||||
} |
||||
}; |
||||
}, |
||||
components: { |
||||
codemirror |
||||
}, |
||||
watch: { |
||||
codeVal(val) { |
||||
this.$emit("update:code", val) |
||||
} |
||||
}, |
||||
mounted() { |
||||
|
||||
}, |
||||
|
||||
methods: { |
||||
// 页面加载完后重置编辑框大小 |
||||
ready() { |
||||
const code = this.$refs.codemirror.codemirror |
||||
code.setSize("auto", "calc(100vh - 167px)"); |
||||
code.on('keypress', function() { |
||||
// 显示智能提示 |
||||
code.showHint() |
||||
}); |
||||
}, |
||||
// 运行代码 |
||||
runCode() { |
||||
let code = this.codeVal |
||||
if (!code) { |
||||
this.$message({ |
||||
message: "警告哦,内容为空不可运行", |
||||
type: "warning" |
||||
}) |
||||
} else { |
||||
const bcId = this.judgmentId |
||||
const cid = this.courseId |
||||
const projectId = this.projectId |
||||
this.loadIns = Loading.service({ |
||||
background: 'transparent' |
||||
}) |
||||
// 如果代码有savefig(python里的保存图片的方法),则要把savefig里的图片名字加上个时间戳,防止后台保存图片到服务器的时候图片名字重复导致覆盖 |
||||
code = code.replace(/\.savefig\(([\u4e00-\u9fa5\w]*?['"])/mg, str => { |
||||
return str + Date.now() |
||||
}) |
||||
// 把代码传给后端,在后端运行Python代码 |
||||
this.$post(this.api.runPythonCode, { |
||||
code, |
||||
bcId, |
||||
cid, |
||||
projectId |
||||
}).then(res => { |
||||
const data = res.code |
||||
const photo = data.photoUrl |
||||
const result = data.runResult |
||||
this.$emit('cache') // 每次运行代码都要把代码传给后端做缓存 |
||||
this.loadIns.close() |
||||
this.picSrcList = [] |
||||
if (photo) this.picSrcList = photo.split(',') |
||||
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 = '' // 对错隐藏 |
||||
const text = result.replace(photo, '') // 结果里包含了图片路径,所以要把图片路径给去掉 |
||||
this.runResult = text |
||||
this.picSrcList = photo.split(',') |
||||
} else if (imgList instanceof Array && imgList.length && typeof firtImg === 'string' && (firtImg.includes('.jpg') || firtImg.includes('.png') || firtImg.includes('img'))) { |
||||
/** |
||||
* 这段是为要下载图片的项目案例写的,后端会返回图片名称的数组,前端负责循环这个数组,然后下载下来 |
||||
* 只有该系统有这段代码,因为其他7个系统没有下载图片的项目,后续如果加了,直接把这段代码复制过去即可 |
||||
*/ |
||||
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 = parseInt(result.substring(result.indexOf("line") + 4, result.length)) |
||||
} |
||||
}).catch(res => { |
||||
res.status == 500 && this.$message.error('检测到代码里有非法代码,请检查是否有调用系统命令。') |
||||
this.loadIns.close() |
||||
}) |
||||
} |
||||
}, |
||||
// 下载图片 |
||||
downloadPic(i) { |
||||
this.$refs['picLink' + i][0].click() |
||||
}, |
||||
// 获取正式答案 |
||||
getTips() { |
||||
this.tipsVisible = true |
||||
this.$get(this.api.queryBcJudgmentByBcId, { |
||||
bcId: this.judgmentId |
||||
}).then(res => { |
||||
this.answer = res.experimentCode |
||||
}).catch(err => {}) |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.wrap { |
||||
display: flex; |
||||
} |
||||
.left{ |
||||
position: relative; |
||||
width: calc(100% - 400px); |
||||
} |
||||
.text-wrapper { |
||||
white-space: pre-wrap; |
||||
} |
||||
/deep/.answer { |
||||
.el-tab-pane { |
||||
padding: 0 10px; |
||||
height: 340px; |
||||
overflow: hidden; |
||||
overflow-y: auto; |
||||
white-space: pre-wrap; |
||||
} |
||||
.el-dialog--center { |
||||
width: 500px; |
||||
height: 500px; |
||||
} |
||||
.el-dialog__title { |
||||
font-size: 22px; |
||||
font-weight: 500; |
||||
} |
||||
.el-tabs__nav-wrap::after { |
||||
background-color: #333; |
||||
} |
||||
.el-tabs__active-bar { |
||||
height: 0; |
||||
background-color: #fff; |
||||
} |
||||
.el-tabs__header { |
||||
background-color: #333; |
||||
} |
||||
.el-dialog--center .el-dialog__body { |
||||
padding: 0; |
||||
} |
||||
.el-tabs__item { |
||||
width: 80px; |
||||
color: #fff; |
||||
} |
||||
.el-tabs--top .el-tabs__item.is-top:nth-child(2) { |
||||
padding-left: 20px; |
||||
} |
||||
.el-tabs__item.is-active { |
||||
color: #fff !important; |
||||
background-color: #333 !important; |
||||
} |
||||
.tips-btn { |
||||
margin-top: 10px; |
||||
height: 40px; |
||||
width: 90px; |
||||
border: none; |
||||
position: absolute; |
||||
right: 0; |
||||
background: rgba(255, 49, 49, 1); |
||||
color: rgba(255, 255, 255, 1); |
||||
&:hover, |
||||
&:focus, |
||||
&:active { |
||||
background: rgba(255, 49, 49, 1); |
||||
color: rgba(255, 255, 255, 1); |
||||
} |
||||
} |
||||
} |
||||
/deep/.CodeMirror-wrap pre.CodeMirror-line, |
||||
.CodeMirror-wrap pre.CodeMirror-line-like { |
||||
height: 30px; |
||||
line-height: 30px; |
||||
} |
||||
.result-right { |
||||
background-color: rgba(43, 40, 22, 1); |
||||
} |
||||
.result-wrong { |
||||
background-color: rgba(43, 22, 22, 1); |
||||
color: #f00; |
||||
} |
||||
.result-wrong, |
||||
.result-right { |
||||
position: absolute; |
||||
left: 20px; |
||||
right: 20px; |
||||
display: flex; |
||||
bottom: 10px; |
||||
padding: 0 10px; |
||||
img { |
||||
width: 40px; |
||||
height: 40px; |
||||
margin-top: 5px; |
||||
margin-right: 10px; |
||||
} |
||||
} |
||||
.code-right { |
||||
width: 500px; |
||||
color: #fff; |
||||
background: #1b1b1b; |
||||
display: inline-block; |
||||
position: relative; |
||||
overflow-x: auto; |
||||
p { |
||||
font-size: 18px; |
||||
margin: 10px; |
||||
position: absolute; |
||||
width: calc(100% - 14px); |
||||
height: calc(100vh - 247px); |
||||
overflow: auto; |
||||
} |
||||
} |
||||
.pic-wrap { |
||||
position: absolute; |
||||
left: 0; |
||||
right: 0; |
||||
bottom: 5px; |
||||
display: flex; |
||||
max-width: calc(100% - 50px); |
||||
margin: 0 auto; |
||||
text-align: center; |
||||
overflow: auto; |
||||
.pic-item { |
||||
margin: 0 5px 5px; |
||||
&:only-child { |
||||
.pic { |
||||
width: 80%; |
||||
} |
||||
} |
||||
} |
||||
.pic { |
||||
display: block; |
||||
width: 100px; |
||||
margin: 0 auto 10px; |
||||
} |
||||
} |
||||
.code-mask{ |
||||
z-index: 2; |
||||
position: absolute; |
||||
top: 0; |
||||
left: 0; |
||||
bottom: 0; |
||||
right: 0; |
||||
} |
||||
.run{ |
||||
z-index:99; |
||||
position:absolute; |
||||
right: 50px; |
||||
bottom:15px; |
||||
width:100px; |
||||
color:#fff; |
||||
} |
||||
.download-btn{ |
||||
color:#fff; |
||||
} |
||||
/deep/.answer-wrap{ |
||||
pre{ |
||||
width: 100%; |
||||
white-space: pre-wrap; |
||||
} |
||||
img{ |
||||
max-width: 100%; |
||||
} |
||||
} |
||||
</style> |
@ -0,0 +1,55 @@ |
||||
<template> |
||||
<div class="wrap"> |
||||
<el-card shadow="hover" class="mgb20"> |
||||
<div class="flex-between"> |
||||
<div class="per_title" v-preventReClick @click="$router.back()"> |
||||
<i class="el-icon-arrow-left"></i> |
||||
<span class="per_back">返回</span> |
||||
<span class="per_school" v-text="isDetail ? '查看源模型' : (customerId ? '编辑源模型' : '新增源模型')"></span> |
||||
</div> |
||||
<el-button v-if="!isDetail" type="primary" round v-preventReClick @click="submit">确定</el-button> |
||||
</div> |
||||
</el-card> |
||||
|
||||
<el-card shadow="hover"> |
||||
<el-form ref="form" label-width="120px" :disabled="isDetail"> |
||||
<el-form-item label="模型名称"> |
||||
<el-input placeholder="请输入模型名称" v-model="form.position" maxlength="25"></el-input> |
||||
</el-form-item> |
||||
<el-form-item label="模型代码"> |
||||
<codemirror |
||||
></codemirror> |
||||
</el-form-item> |
||||
</el-form> |
||||
</el-card> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import codemirror from '@/components/codemirror' |
||||
export default { |
||||
components:{ codemirror }, |
||||
data() { |
||||
return { |
||||
customerId : this.$route.query.id, |
||||
isDetail : Boolean(this.$route.query.show), |
||||
form: { |
||||
|
||||
} |
||||
}; |
||||
}, |
||||
mounted() { |
||||
|
||||
}, |
||||
methods: { |
||||
// 保存 |
||||
submit(){ |
||||
|
||||
}, |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
|
||||
</style> |
Loading…
Reference in new issue