You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1148 lines
37 KiB

<template>
<div class="code-wrap flex">
<div class="left">
<codemirror v-model="codeVal"
:options="cmOption"
class="code-mirror"
@ready="ready"
ref="codemirror"></codemirror>
<div v-if="isSubmit"
class="code-mask"></div>
<div class="btns">
<span class="el-icon-delete del"
@click="clearCode"></span>
<el-button v-if="isPrac"
class="btn"
type="danger"
@click="myCode">我的代码</el-button>
<el-button v-if="modelIsShow"
class="btn"
type="warning"
@click="importModel">导入模型</el-button>
<el-button class="run btn"
type="primary"
@click="runCode(false)"
:disabled="runEnable">运行</el-button>
</div>
</div>
<div class="line"></div>
<div class="code-right answer">
<p :class="['text-wrapper', 'pic-num' + picSrcList.length]">{{ runResult }}</p>
<div :class="['pic-wrap', {wrong: isError === 0}]"
v-if="picSrcList.length">
<div class="pic-item"
v-for="(img, i) in picSrcList"
:key="i">
<div class="pic-inner">
<el-image class="pic"
:src="img"
:preview-src-list="picSrcList">
</el-image>
</div>
<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/${$themeId}/yes.png`)"
alt />运行成功
<el-button class="tips-btn"
@click="exportResult">导出结果</el-button>
</div>
<div class="result-wrong"
v-show="isError === 0">
<img src="@/assets/images/error.png"
alt />
第{{errLine}}行出现错误
<el-button class="tips-btn"
@click="getTips"
v-show="isPrac">提示</el-button>
<el-dialog title="答案提示"
center
:close-on-click-modal="false"
:visible.sync="tipsVisible">
<el-tabs>
<el-tab-pane label="参考答案">
<div :class="['answer-wrap', {client: !fromManager}]"
v-html="answer"></div>
</el-tab-pane>
</el-tabs>
</el-dialog>
</div>
</div>
<!-- 导入模型 -->
<el-dialog title="请选择要导入的模型"
:visible.sync="modelVisible"
width="400px"
:close-on-click-modal="false"
custom-class="model-dia">
<el-select class="w-100"
v-model="modelType"
size="mini">
<el-option v-for="item in modelOp"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
<!-- <el-radio-group v-model="modelType"
@change="changeModelType">
<div style="margin-bottom: 10px;">
<el-radio :label="0">本校模型</el-radio>
</div>
<div>
<el-radio :label="1">内置模型</el-radio>
</div>
</el-radio-group> -->
<!-- <el-divider></el-divider> -->
<div class="model-wrap">
<el-tree v-if="!modelType"
:data="schoolModel"
ref="tree"
default-expand-all
@check-change="treeCheckChange"
show-checkbox
:check-strictly="true"
node-key="id"
:props="{children: 'children', label: 'categoryName', isLeaf: 'leaf'}"
v-loading="modelLoading">
</el-tree>
<el-tree v-if="modelType"
:data="systemModel"
ref="tree"
default-expand-all
@check-change="treeCheckChange"
show-checkbox
:check-strictly="true"
node-key="id"
:props="{children: 'children', label: 'categoryName', isLeaf: 'leaf'}"
v-loading="modelLoading">
</el-tree>
</div>
<span slot="footer"
class="dialog-footer">
<el-button size="small"
@click="modelVisible = false">取消</el-button>
<el-button size="small"
type="primary"
@click="submit">导入</el-button>
</span>
</el-dialog>
<el-dialog title="我的代码"
:visible.sync="codeVisible"
width="1200px"
:close-on-click-modal="false"
custom-class="code-dia">
<div class="tool">
<el-input style="width: 250px"
placeholder="请输入项目名称、判分点名称"
v-model="keyword"
suffix-icon="el-icon-search"
clearable
size="small"></el-input>
<div class="action">
<el-button class="cus-btn"
type="primary"
size="small"
@click="delAll">批量删除</el-button>
</div>
</div>
<el-table :data="codeList"
class="cus-table"
ref="table"
stripe
max-height="400"
header-align="center"
@selection-change="handleSelectionChange">
<el-table-column type="selection"
width="60"
align="center"></el-table-column>
<el-table-column type="index"
label="序号"
width="55"
align="center"></el-table-column>
<el-table-column prop="judgmentName"
label="判分点"
min-width="250"
align="center"
show-overflow-tooltip></el-table-column>
<el-table-column prop="projectName"
label="项目名称"
min-width="250"
align="center"
show-overflow-tooltip></el-table-column>
<el-table-column prop="submitTime"
label="提交时间"
width="140"
align="center"></el-table-column>
<el-table-column prop="score"
label="是否得分"
width="80"
align="center"></el-table-column>
<el-table-column width="190"
label="操作"
align="center">
<template slot-scope="scope">
<el-button type="text"
@click="showCode(scope.row)">查看代码</el-button>
<el-button type="text"
@click="importCode(scope.row)">导入代码</el-button>
<el-button type="text"
@click="del(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination background
layout="total,prev, pager, next"
:current-page="page"
@current-change="handleCurrentChange"
:total="total"></el-pagination>
</div>
<span slot="footer"
class="dialog-footer">
<el-button size="small"
type="primary"
@click="codeVisible = false">关闭</el-button>
</span>
</el-dialog>
<el-dialog title="查看代码"
:visible.sync="showVisible"
width="700px"
:close-on-click-modal="false"
custom-class="code-dia">
<el-tabs class="code-tab"
v-model="showActive">
<el-tab-pane label="代码"
name="code">
<codemirror class="code"
v-model="curCode"
:options="cmOptionDia"></codemirror>
</el-tab-pane>
<el-tab-pane label="结果"
name="result">
<div style="position: relative">
<div class="result"
v-html="curResult"></div>
<div v-if="curPhotoUrl.length"
class="pics">
<div class="pic-item"
v-for="(img, i) in curPhotoUrl"
:key="i">
<el-image class="pic"
:src="img"
:preview-src-list="curPhotoUrl">
</el-image>
</div>
</div>
</div>
</el-tab-pane>
</el-tabs>
<span slot="footer"
class="dialog-footer">
<el-button size="small"
type="primary"
@click="showSubmit">导入代码</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import Cookie from 'js-cookie'
import newmain from "../util/newMain";
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';
import axios from 'axios'
import util from '@/util'
import config from '@/config'
import { saveAs } from 'file-saver'
const CANCEL_TOKEN = axios.CancelToken // 用于input中中断请求
export default {
props: ['judgmentId', 'code', 'codeId', 'projectId', 'systemId', 'retResult', 'modelIsShow', 'photoUrl'],
data () {
return {
token: Cookie.get('admin-token'),
assessmentId: Cookie.get('admin-assessmentId'), // 考核id
competitionId: Cookie.get('admin-competitionId'),
fromManager: Cookie.get('admin-fromManager'), // 是否是从教师端进入
courseId: Cookie.get('admin-courseId'), // 课程id
mallId: Cookie.get('admin-mallId'),
isPrac: false, // 练习
answer: '', // 正确答案
codeVal: this.code,
runResult: '', // 运行结果
isError: false, // 运行正确与否
errLine: '', // 错误代码的位置
isSubmit: Cookie.get('admin-isSubmit') == 'true' ? true : false, // 是否提交的标识
runEnable: false, // 是否禁用运行按钮
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" // 主题
},
cmOptionDia: {
readOnly: true,
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" // 主题
},
inputTextReg: /^((?!#).*?(,|\s?|\(|\[|\{)+)?input(?!\w)\(['|"]([\s\S]+?)['|"]\)/m, // 匹配input()
requestList: [], // 有input的情况下,保存每个axios的对象,用于中断请求
sourceCode: '', // 把input替换成exit函数后的代码
requestTimer: null, // 用于中断请求的定时器
modelVisible: false,
modelLoading: false,
modelImporting: false,
schoolModel: [],
systemModel: [],
modelType: 1,
modelOp: [
{
id: 0,
name: '本校模型'
},
{
id: 1,
name: '内置模型'
},
],
codeVisible: false,
codeList: [],
multipleSelection: [],
keyword: '',
page: 1,
pageSize: 10,
total: 0,
searchTimer: null,
showVisible: false,
showActive: 'code',
curCode: '',
curResult: '',
curPhotoUrl: [],
};
},
components: {
codemirror
},
watch: {
codeVal (val) {
this.$emit("update:code", val)
},
keyword: function (val) {
clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => {
this.initData()
}, 500)
},
},
mounted () {
if (!this.assessmentId && !this.competitionId) this.isPrac = true
//兄弟组件传值
newmain.$on("isSubmit", isSubmit => {
this.isSubmit = isSubmit
})
this.dragSide()
},
methods: {
// 页面加载完后重置编辑框大小
ready () {
const code = this.$refs.codemirror.codemirror
code.setSize("auto", "calc(100vh - 167px)");
// 查询粘贴状态
this.$post(`${this.api.whetherCanPaste}?systemId=${this.systemId}`).then(res => {
// 监听改变前的钩子,禁止粘贴(从学生端进入,并且老师端禁用了粘贴功能)
res.data == 'false' && !this.fromManager && code.on('beforeChange', (istance, change) => {
change.origin === 'paste' && change.cancel()
})
}).catch(err => { })
code.on('keypress', function () {
// 显示智能提示
code.showHint()
});
},
// 清屏
clearCode () {
this.codeVal = ''
this.isError = ''
this.runResult = ''
this.$emit('update:codeId', '') // 更新coddeId
this.$emit('update:answer', '') // 更新运行结果
this.$emit('update:retResult', '') // 更新返回结果
},
// 导入模型
importModel () {
this.modelLoading = true
this.modelVisible = true
// 查询源模型分类
this.$post(`${this.api.modelClassListByStudent}?systemId=${this.systemId}&founder=1`).then(res => {
const promises = []
const addType = (list, system) => {
list.map(e => {
e.disabled = true
// 用promise储存以添加完后更新数据
promises.push(new Promise((resolve, reject) => {
// 系统内置跟本校的模型接口不一样
this.$post(this.api[system ? 'studentModelListBySystem' : 'studentModelList'], {
categoryId: e.id,
pageNum: 1,
pageSize: 10000,
systemId: +this.systemId,
founder: 1
}).then(({ data }) => {
let { records } = data
records = records.filter(e => !e.isOpen) // 禁用的不显示
records.map(n => {
n.categoryName = n.modelName
})
e.children = [...e.children, ...records]
resolve()
}).catch(res => {
reject()
})
}))
e.children && e.children.length && addType(e.children, system)
})
}
addType(res.schoolClassification)
addType(res.systemBuiltInClassification, 1)
Promise.all(promises).then(_ => {
this.schoolModel = res.schoolClassification
this.systemModel = res.systemBuiltInClassification
this.modelLoading = false
}).catch(res => { })
}).catch(res => { })
},
// 分类类型选择回调
changeModelType (val) {
// this.getType()
},
// 多选框选择回调
treeCheckChange (data, checked, indeterminate) {
const checkKey = this.$refs.tree.getCheckedKeys()
// 这里要求单选,所以把多选框改成单选,选择了一个节点后把之前选的都给取消选择,再选中刚勾选的
if (checkKey.length > 1 && checked) {
this.$refs.tree.setCheckedNodes(checkKey, false)
this.$refs.tree.setChecked(data.id, true)
}
},
// 导入模型提交
submit () {
if (this.modelImporting) return false
const id = this.$refs.tree.getCheckedKeys()
if (!id.length) return this.$message.error('请选择模型!')
this.modelImporting = true
this.$post(`${this.api.referenceFindById}?id=${id[0]}`).then(res => {
this.codeVal += (this.codeVal ? '\n\n' : '') + res.data.modelDemo // 空两行插入
this.modelVisible = false
this.$nextTick(() => {
const codemirror = this.$refs.codemirror.codemirror
codemirror.focus()
codemirror.setCursor(codemirror.lineCount(), 0)
setTimeout(() => {
this.modelImporting = false
}, 2000)
})
}).catch(res => { })
},
/**
* python代码里如果有input函数的话,是做了单独的处理的,原理是先把所有input函数都替换成exit函数,再在exit函数里加上特定标识,再通过接口传给后端去执行
* 因为exit函数是跟input有类似的效果,就是都会产生阻塞,所以python引擎一旦遇到exit,进程就会被停止,然后返回exit函数里面的值,而这个值,就是上面说的特定标识加上原本这个input函数里的值
* 然后就可以通过这个返回的值来提示给用户,让用户继续输入
* 下面这个函数就是递归执行这个input输入过程的函数
*/
confirmInput (msg) {
const receiveResult = msg.replace('validing:', '')
this.runResult += receiveResult
this.$prompt(receiveResult, '提示', {
confirmButtonText: "确定"
}).then(({ value }) => {
this.runResult += `${value}\n`
// 把exit函数替换成用户输入的值
this.sourceCode = this.sourceCode.replace(`exit('validing:${receiveResult.replace(/[\r\n]*/g, '')}')`, _ => {
return `'${value}'`
})
this.sourceCode = this.sourceCode.replace(`exit("validing:${receiveResult.replace(/[\r\n]*/g, '')}")`, _ => {
return `'${value}'`
})
clearTimeout(this.requestTimer)
// 一秒钟后还没请求成功的话,就直接中断请求,因为一般是陷入死循环了才会需要这么长时间的,中断了后就再次执行AnswerTips(运行按钮的执行函数)函数,再次进入循环
this.requestTimer = setTimeout(() => {
this.requestList.map(n => n('interrupt'))
}, 1000)
axios.post(config.host + this.api.runPythonCode, {
code: this.sourceCode,
bcId: this.judgmentId,
cid: this.courseId,
projectId: this.projectId,
type: 0
}, {
headers: {
token: this.token
},
cancelToken: new CANCEL_TOKEN(c => { //强行中断请求要用到的,记录请求信息
this.requestList.push(c)
})
}).then(response => {
let res = response.data
const data = res.code
const result = data.runResult
if (result.includes('File ')) {
this.runResult = result
this.errLine = parseInt(result.substring(result.indexOf('line') + 4, result.length))
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', 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 = []
}
})
}).catch(err => { })
},
// 运行代码
runCode (isWhile) { // isWhile为true表示代码里有while循环,右边的运行结果需要拼接展示,而不是直接覆盖
if (!this.isSubmit) {
let code = this.codeVal
if (!code) {
this.$message({
message: "警告哦,内容为空不可运行",
type: "warning"
})
} else {
const { inputTextReg } = this
const inputFuncReg = /input\(['|"]/g
const bcId = this.judgmentId
const cid = this.courseId
const projectId = this.projectId
// 该正则是验证代码里是否有input,如果有,就要另外做处理,而不是直接传给后端执行
if (inputTextReg.test(code)) {
code = code.replace(inputTextReg, val => {
return val.replace(/\\n/g, "")
})
this.codeVal = code
// 把input函数替换成exit函数,加上"validing:"作为特定标识,好方便后面的识别
code = code.replace(inputFuncReg, val => {
return `exit(${val[val.length - 1]}validing:`
})
this.sourceCode = code
if (!isWhile) this.runResult = ''
this.$post(this.api.runPythonCode, {
code,
bcId,
cid,
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 = parseInt(result.substring(result.indexOf('line') + 4, result.length))
this.isError = data.retResult
} else if (result.includes('validing:')) {
this.isError = 1
this.confirmInput(result)
}
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'
})
// 如果代码有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,
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: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 = parseInt(result.substring(result.indexOf("line") + 4, result.length))
} 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 = parseInt(result.substring(result.indexOf("line") + 4, result.length))
}
} else if (res.data && res.data.current) {
// 进入运行队列
this.$message.success(`当前队列有${res.data.current - 1}人在排队,请稍等!`)
this.loadIns.close()
}
}).catch(res => {
this.isError = false
this.runResult = ''
this.picSrcList = []
this.loadIns.close()
res.status == 500 && this.$message.error('检测到代码里有非法代码,请检查是否有调用系统命令。')
})
}
}
} else {
this.runEnable = true
this.$message({
message: "警告哦,已提交不可再运行",
type: "warning"
})
}
},
// 下载图片
downloadPic (i) {
util.downloadPic('运行结果.png', this.picSrcList[i])
// this.$refs['picLink' + i][0].click()
},
// 导出运行结果
exportResult () {
let str = this.runResult
var blob = new Blob(["\ufeff" + str], { type: 'text/csv;charset=utf-8;' });
saveAs(blob, 'result.csv')
},
// 获取正式答案
getTips () {
this.tipsVisible = true
this.$get(this.api.queryBcJudgmentByBcId, {
bcId: this.judgmentId
}).then(res => {
this.answer = res.experimentCode
}).catch(err => { })
},
// 中间拖拽
dragSide () {
const left = document.querySelector('.left')
const line = document.querySelector('.line')
const right = document.querySelector('.code-right')
// 移动区域鼠标移入事件
line.onmousedown = e => {
// 外面容器宽度
const width = document.querySelector('.code-wrap').clientWidth
document.onmousemove = e => {
let x = e.clientX
// 移动区间的范围
if (x >= width * .1 && x <= width * .9) {
line.style.left = x + 'px'
left.style.width = x + 'px'
right.style.width = document.querySelector('.code-wrap').clientWidth - (x + 5) + 'px'
}
}
document.onmouseup = () => {
document.onmousemove = null
document.onmouseup = null
}
}
},
// 获取我的代码
async getCodes () {
const { page } = await this.$post(`${this.api.importCode}?mallId=${this.mallId}&curriculumId=${this.courseId}&projectId=${this.projectId}&current=${this.page}&size=${this.pageSize}&projectName=${this.keyword}`)
this.codeList = page.records
this.total = page.total
},
// 初始化列表
initData () {
this.page = 1
this.getCodes()
},
// 我的代码
myCode () {
this.initData()
this.codeVisible = true
},
// 批量删除
delAll () {
if (this.multipleSelection.length) {
const newArr = this.multipleSelection
const ids = newArr.map(e => {
return `importId=${e.importId}`
})
this.$confirm(`此批量删除操作不可逆,是否确认删除?`, '提示', {
type: 'warning'
}).then(() => {
this.$post(this.api.removeImport + '?' + ids.join('&')).then(res => {
this.$refs.table.clearSelection()
this.$message.success('删除成功')
this.getCodes()
}).catch(res => { })
}).catch(() => { })
} else {
this.$message.error('请先选择数据 !')
}
},
// 删除
del (row) {
this.$confirm('此删除操作不可逆,是否确认删除选中项?', '提示', {
type: 'warning'
}).then(() => {
this.$post(this.api.removeImport + '?importId=' + row.importId).then(res => {
this.$message.success('删除成功')
this.getCodes()
}).catch(res => { })
}).catch(() => { })
},
handleCurrentChange (val) {
this.page = val
this.getCodes()
},
handleSelectionChange (val) {
this.multipleSelection = val
},
// 查看代码
showCode (row) {
this.curCode = row.runCode
this.curResult = row.runResults
this.curPhotoUrl = row.photoUrl ? row.photoUrl.split(',') : []
this.showVisible = true
},
// 导入代码
importCode (row) {
this.codeVal += (this.codeVal ? '\n' : '') + row.runCode // 空两行插入
this.$nextTick(() => {
const codemirror = this.$refs.codemirror.codemirror
codemirror.focus()
codemirror.setCursor(codemirror.lineCount(), 0)
})
this.codeVisible = false
},
// 导入代码
showSubmit () {
this.codeVal += (this.codeVal ? '\n' : '') + this.curCode // 空两行插入
this.$nextTick(() => {
const codemirror = this.$refs.codemirror.codemirror
codemirror.focus()
codemirror.setCursor(codemirror.lineCount(), 0)
})
this.showVisible = false
this.codeVisible = false
},
}
};
</script>
<style lang="scss" scoped>
.code-wrap {
position: relative;
}
.left {
position: relative;
width: 60%;
}
.text-wrapper {
white-space: pre-wrap;
}
/deep/.CodeMirror-code {
font-family: 'Microsoft Yahei';
}
/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);
}
}
}
.del {
width: 40px;
margin: 0 10px;
line-height: 40px;
color: #fff;
text-align: center;
border-radius: 50%;
background-color: #f00;
cursor: pointer;
&:hover {
opacity: 0.9;
}
}
.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;
}
}
.line {
z-index: 2;
position: absolute;
left: 60%;
top: 0;
width: 5px;
height: 100%;
cursor: e-resize;
}
.code-right {
width: 40%;
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 - 387px);
overflow: auto;
}
.pic-num0 {
height: calc(100vh - 247px);
}
.pic-num1 {
height: calc(100vh - 520px);
}
}
.pic-wrap {
position: absolute;
left: 0;
right: 0;
bottom: 60px;
display: flex;
max-width: calc(100% - 50px);
margin: 0 auto;
text-align: center;
overflow: auto;
&.wrong {
bottom: 60px;
}
.pic-item {
margin: 0 5px 5px;
.pic-inner {
height: 100px;
}
&:only-child {
.pic-inner {
height: auto;
}
.pic {
width: 50%;
max-height: none;
}
}
}
.pic {
display: block;
width: 100px;
max-height: 100px;
object-fit: cover;
margin: 0 auto 10px;
}
}
.code-mask {
z-index: 2;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
.btns {
z-index: 99;
position: absolute;
right: 50px;
bottom: 15px;
display: flex;
}
.run {
width: 100px;
color: #fff;
}
.download-btn {
color: #fff;
}
/deep/.answer-wrap {
&.client {
user-select: none;
}
pre {
width: 100%;
white-space: pre-wrap;
}
img {
max-width: 100%;
}
}
/deep/.model-dia {
.el-dialog__body {
padding: 0 20px;
}
.el-divider--horizontal {
margin: 15px 0;
}
}
.model-wrap {
max-height: 400px;
overflow: auto;
}
/deep/.code-dia {
.el-dialog__header {
padding: 0 20px;
}
.el-dialog__body {
padding: 0 20px;
}
.code-tab {
.el-tabs__item.is-active {
color: #333 !important;
background-color: transparent;
}
}
.tool {
display: flex;
justify-content: space-between;
}
.code {
margin-top: 10px;
}
.result {
font-size: 18px;
white-space: pre-wrap;
line-height: 50px;
color: #333;
}
.pics {
display: flex;
margin-top: 20px;
overflow: auto;
.pic-item {
margin: 0 5px 5px;
&:only-child {
.pic {
width: 50%;
max-height: none;
}
}
}
.pic {
display: block;
width: 100px;
max-height: 100px;
object-fit: cover;
margin: 0 auto 10px;
}
}
}
</style>