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.

1347 lines
46 KiB

4 years ago
<template>
2 years ago
<div :class="['panel', {active: pannelVisible}]"
id="panel">
<el-container class="scrollbar"
2 years ago
id="container"
v-show="pannelVisible">
2 years ago
<el-header id="header">
2 years ago
<div class="panel-header"
id="panelHeader">
3 years ago
<div class="project">
<div class="inline-center">
<p>实训项目</p>
2 years ago
<el-tooltip effect="dark"
content="点击右侧“下三角”按钮可切换实验项目"
placement="bottom">
<i class="info el-icon-warning"
style="margin-left: 10px"></i>
</el-tooltip>
</div>
2 years ago
<el-select v-model="projectId"
placeholder="请选择"
class="select"
:disabled="projectPermissions != 0"
@change="selectProject"
style="flex: 1">
<el-option v-for="(item, i) in projectList"
:key="item.projectId"
:label="i + 1 + '. ' + item.projectName"
:value="item.projectId"></el-option>
4 years ago
</el-select>
</div>
3 years ago
<div class="item">
<div class="count">
实训{{text}}时间
<span>{{day}}</span>
<span>{{hour}}</span>小时
<span>{{minutes}}</span>
<span>{{seconds}}</span>
4 years ago
</div>
</div>
2 years ago
<div v-if="!competitionId"
class="item">
4 years ago
<div>
总得分
<span class="total-score">{{grade}}</span>
4 years ago
</div>
</div>
<div>
2 years ago
<el-button @click="toggleReport"
v-if="$parent.language && !isSubmit && !competitionId">填写实验报告</el-button>
2 years ago
<el-button @click="toReport"
v-if="isSubmit && !competitionId">查看实验报告</el-button>
<el-button class="reload"
@click="reload"
v-show="projectPermissions == 0">重新开始</el-button>
<el-button type="primary"
class="submit btn"
@click="confirmSubmit"
:disabled="isSubmit || !projectList.length">提交</el-button>
4 years ago
</div>
</div>
</el-header>
2 years ago
<div class="report-wrap"
v-if="reportVisible">
<i class="el-icon-caret-top close-report"></i>
<el-table class="report-table"
:data="taskList"
:max-height="tableHeight">
<el-table-column prop="name"
label="判分点"
width="210"
align="center"></el-table-column>
<el-table-column prop="score"
label="答案"
align="center">
<template slot-scope="scope">
<codemirror v-model="scope.row.code"
:options="cmOption"
class="code-mirror"
:ref="'codemirror' + scope.$index"
@ready="ready"></codemirror>
</template>
</el-table-column>
<el-table-column prop="score"
label="运行结果"
align="center">
<template slot-scope="scope">
<quill class="quill"
:tools="false"
:border="true"
v-model="scope.row.codeResult"
:height="300"
:index="1" />
</template>
</el-table-column>
</el-table>
</div>
2 years ago
<el-container id="infoContainer"
v-else>
<el-aside id="aside"
width="30%">
4 years ago
<div class="aside-header">
2 years ago
<div :class="['p-title color', 'system' + $themeId]">
4 years ago
<i class="el-icon-s-order"></i>
<p>实验目标</p>
</div>
3 years ago
<div class="goal">
<div v-if="pd.experimentTargetType == 0 || !pd.experimentTargetType"
1 year ago
class="ql-snow">
<div class="ql-editor"
v-html="pd.experimentTarget"></div>
</div>
<mavon-editor v-else
class="md"
v-model="pd.experimentTarget"
1 year ago
defaultOpen="preview"
:ishljs="true"
:subfield="false"
:editable="false"
:toolbarsFlag="false"
:boxShadowStyle="none" />
4 years ago
</div>
</div>
<div class="aside-footer">
2 years ago
<div :class="['p-title color', 'system' + $themeId]">
4 years ago
<i class="el-icon-s-management"></i>
<p>实验任务</p>
</div>
<div>
<el-row>
<el-col :span="24">
2 years ago
<el-card shadow="never"
:border="false">
2 years ago
<el-table class="task-table"
:data="taskList"
2 years ago
:stripe="true">
4 years ago
<el-table-column type="index"></el-table-column>
2 years ago
<el-table-column prop="name"
label="判分点"
align="center"></el-table-column>
<el-table-column prop="score"
label="分值"
width="60"
align="center"></el-table-column>
<template v-if="!competitionId">
2 years ago
<el-table-column label="结果"
width="60"
align="center">
<template slot-scope="scope">
<template v-if="isSubmit">
<template v-if="!competitionId">
2 years ago
<i v-if="scope.row.finishedResult"
class="el-icon-check right"></i>
<i v-else
class="el-icon-close wrong"></i>
</template>
<template v-else>-</template>
</template>
</template>
</el-table-column>
2 years ago
<el-table-column prop="score"
label="得分"
width="60"
align="center">
<template slot-scope="scope">
<template v-if="isSubmit">{{ competitionId ? '-' : scope.row.examScore }}</template>
3 years ago
</template>
</el-table-column>
</template>
4 years ago
</el-table>
</el-card>
</el-col>
</el-row>
</div>
</div>
</el-aside>
2 years ago
<el-main id="main">
2 years ago
<el-tabs class="info-tab"
v-model="pannelTab"
2 years ago
type="card">
<el-tab-pane label="项目背景"
name="first">
<div v-if="pd.experimentDescriptionType == 0 || !pd.experimentDescriptionType"
1 year ago
class="ql-snow">
<div class="ql-editor"
v-html="pd.experimentDescription"></div>
</div>
<mavon-editor v-else
class="md"
v-model="pd.experimentDescription"
1 year ago
defaultOpen="preview"
:subfield="false"
:editable="false"
:toolbarsFlag="false"
:boxShadowStyle="none" />
4 years ago
</el-tab-pane>
2 years ago
<el-tab-pane label="实验要求"
name="second">
<el-collapse v-model="curReq">
2 years ago
<el-collapse-item v-for="item in points"
:name="item.judgmentId"
:key="item.judgmentId">
4 years ago
<template slot="title">
<i class="el-icon-s-ticket"></i>
2 years ago
<div class="break-all des"
v-html="item.name"></div>
4 years ago
</template>
<div v-if="item.experimentalRequirementsType == 0 || !item.experimentalRequirementsType"
1 year ago
class="ql-snow">
<div class="ql-editor"
v-html="item.experimentalRequirements"></div>
</div>
<mavon-editor v-else
class="md"
v-model="item.experimentalRequirements"
1 year ago
defaultOpen="preview"
:ishljs="true"
:subfield="false"
:editable="false"
:toolbarsFlag="false"
:boxShadowStyle="none" />
4 years ago
</el-collapse-item>
</el-collapse>
</el-tab-pane>
2 years ago
<el-tab-pane label="实验提示"
2 years ago
name="third"
2 years ago
v-if="hintOpen">
<div v-if="pd.experimentHintType == 0 || !pd.experimentHintType"
1 year ago
class="ql-snow">
<div class="ql-editor"
v-html="pd.experimentHint"></div>
</div>
<mavon-editor v-else
class="md"
v-model="pd.experimentHint"
1 year ago
defaultOpen="preview"
:ishljs="true"
:subfield="false"
:editable="false"
:toolbarsFlag="false"
:boxShadowStyle="none" />
4 years ago
</el-tab-pane>
</el-tabs>
</el-main>
</el-container>
</el-container>
<div :class="['toggle-panel', {active: pannelVisible}]"
id="toggle"
@click="togglePannel">
<!-- <div @click="togglePannel"> -->
<div>
2 years ago
<img :src="require(`@/assets/images/system/${$themeId}/left.png`)"
2 years ago
alt
class="c-p"
v-if="pannelVisible" />
2 years ago
<img :src="require(`@/assets/images/system/${$themeId}/right.png`)"
2 years ago
alt
class="c-p"
v-if="!pannelVisible" />
4 years ago
</div>
</div>
</div>
</template>
<script>
import newmain from "../util/newMain";
2 years ago
import quill from "@/components/quill";
import util from '@/util'
3 years ago
import Config from '@/config'
import Cookie from 'js-cookie'
2 years ago
import 'quill/dist/quill.core.css';
import 'quill/dist/quill.snow.css';
import 'quill/dist/quill.bubble.css';
2 years ago
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 { mavonEditor } from 'mavon-editor'
import 'mavon-editor/dist/css/index.css'
4 years ago
export default {
2 years ago
data () {
4 years ago
return {
3 years ago
token: Cookie.get('admin-token'),
systemId: Cookie.get('admin-systemId') || 1,
classId: Cookie.get('admin-classId'),
className: Cookie.get('admin-className') ? decodeURI(Cookie.get('admin-className')) : '',
courseId: Cookie.get('admin-courseId'),
projectId: Cookie.get('admin-projectId') ? Number(Cookie.get('admin-projectId')) : '',
assessmentId: Cookie.get('admin-assessmentId'),
competitionId: Cookie.get('admin-competitionId'),
stageId: Cookie.get('admin-stageId'),
teamId: Cookie.get('admin-teamId'),
2 years ago
mallId: Cookie.get('admin-mallId'),
resultsDetails: Cookie.get('admin-resultsDetails'),
resultAnnouncementTime: Cookie.get('admin-resultAnnouncementTime'),
3 years ago
curriculumName: Cookie.get('admin-curriculumName') ? unescape(Cookie.get('admin-curriculumName')) : 'python', // 课程名称
2 years ago
fromManager: Cookie.get('admin-fromManager'), // 是否是从教师端进入
curSystemId: 1,
3 years ago
projectPermissions: 0, // 项目权限(0、练习 1、考核 2、竞赛)
2 years ago
isSubmit: Cookie.get('admin-isSubmit') == 'true' ? true : false, // 是否提交的标识
entryTime: new Date(),
startTime: Cookie.get('admin-startTime'),
endTime: Cookie.get('admin-stopTime'),
pannelVisible: true, // 实验面板显示标识
grade: '00', // 得分
text: '', // 倒计时前面的文字,练习:所用;考核:剩余。练习是计时,考核是倒计时
counterTimer: null, // 获取setInterval对象值
day: 0, // 天数
seconds: 0, // 秒数
minutes: 0, // 分钟数
hour: 0, // 小时数
projectList: [], // 项目列表
pd: {},
experimentTarget: '', //实验目标
experimentDescription: '', //案例描述
experimentHint: '', //实验提示
hintOpen: 1, // 是否显示实验提示
points: [], // 判分点列表
judgmentId: '', // 当前判分点id
curReq: [], // 当前实验要求
taskList: [], // 实验任务列表
pannelTab: 'first', // 面板信息切换值
isSelected: false, // 是否选择过项目的标识,选择了会置为true
statusTimer: null, // 查询考核状态定时器
2 years ago
reportId: '',
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" // 主题
},
reportTab: '',
reportPoints: [],
reportVisible: false,
2 years ago
tableHeight: 0,
dragIds: ['panelHeader', 'aside', 'main', 'infoContainer']
4 years ago
};
},
2 years ago
components: {
codemirror,
quill,
mavonEditor
2 years ago
},
2 years ago
mounted () {
// 2:竞赛,1:考核,0:练习
this.projectPermissions = this.assessmentId ?
1 :
this.competitionId ?
2 :
2 years ago
0
if (this.assessmentId) { // 考核(考核才会从外面带进来assessmentId,练习是默认显示第一个项目,竞赛会带进来competitionId)
this.getAssList()
} else { // 练习
2 years ago
if (Cookie.get('doneProjectId')) {
this.projectId = +Cookie.get('doneProjectId')
}
Cookie.remove('doneProjectId')
3 years ago
// 获取项目列表
this.getList().then(() => {
let cache = localStorage.getItem('codeCache') // 获取本地缓存
// 如果有缓存,再调接口取上次运行的代码
if (cache) {
this.getCache(JSON.parse(cache))
} else {
this.getCache()
3 years ago
}
2 years ago
}).catch(res => { })
if (this.competitionId) {
clearInterval(this.statusTimer)
this.statusTimer = setInterval(_ => {
this.getCompetitionStatus()
}, 1000)
}
4 years ago
}
// this.drag()
2 years ago
this.tableHeight = window.innerHeight - 360
4 years ago
},
methods: {
// 获取项目列表
2 years ago
getList () {
let data = {
3 years ago
systemId: this.systemId,
cId: this.courseId, // 课程id
2 years ago
mallId: this.mallId,
permissions: this.projectPermissions // 练习/考核/竞赛
}
return new Promise((resolve, reject) => {
2 years ago
this.$get(`${this.api.queryTestProject}`, data).then(res => {
const list = res.projects
this.projectList = list
3 years ago
if (!this.projectPermissions && !this.projectId) this.projectId = list ? list[0].projectId : 0 // 默认取第一个项目
this.getProDetail().then(() => {
resolve()
}).catch(res => {
reject()
3 years ago
})
}).catch(res => {
reject()
4 years ago
})
})
4 years ago
},
3 years ago
// 获取项目详情
2 years ago
getProDetail () {
2 years ago
const { projectId } = this
3 years ago
return new Promise((resolve, reject) => {
3 years ago
this.$get(this.api.getProjectDetail, {
3 years ago
projectId,
stuAssessent: 1
3 years ago
}).then(res => {
const points = res.projectJudgmentVos
const project = res.projectManage
const curReq = []
3 years ago
// 跳转银行
if (project.systemId == 11) {
return location.href = `${Config.bankPath}/#/index/list?curriculumName=${this.curriculumName}&token=${this.token}&cid=${this.courseId}&systemId=${this.systemId}&projectId=${projectId}&assessmentId=&classId=&stopTime=&test=true`
}
3 years ago
if (!points.length) {
this.$message.error('该项目没有判分点,请换个项目重试')
this.projectId = this.projectList[0].projectId
2 years ago
// this.getProDetail()
3 years ago
}
points.map((e, i) => {
e.code = '' // 后端返回的字段没有code,要手动加上以存储运行的代码
e.codeId = '' // 代码通过接口传给后端运行后,接口会返回一个codeId,提交的时候需要传这个codeId
2 years ago
e.answer = '' // 代码运行结果
e.retResult = '' // 返回结果(1为正确 0为错误)
curReq.push(e.judgmentId)
3 years ago
})
// 考核/竞赛
if (this.projectPermissions) {
this.projectList = [{
3 years ago
projectId,
projectName: project.projectName
}]
}
const { systemId } = project
this.curSystemId = systemId
this.$parent.getModelStatus(systemId)
this.curReq = curReq // 实验要求默认全部展开,通过judgmentId来选中item
3 years ago
this.points = points
2 years ago
const reportPoints = JSON.parse(JSON.stringify(points))
points.forEach(e => {
e.codeResult = ''
})
this.reportPoints = reportPoints
if (points.length) this.reportTab = reportPoints[0].judgmentId + ''
2 years ago
this.taskList = this.isSubmit ? this.$store.state.taskList : points // 实验任务
this.judgmentId = points[0].judgmentId // 默认取第一个判分点
this.pd = project
3 years ago
this.experimentTarget = project.experimentTarget
this.experimentDescription = project.experimentDescription
this.experimentHint = project.experimentHint
this.hintOpen = project.founder ? !project.hintOpenBySchool : !project.hintOpen // 0显示,1不显示,系统跟老师的禁用字段不一样
this.$emit('tell', projectId, systemId, this.points)
const isPrac = this.projectPermissions == 0 // 是否是练习
this.text = isPrac ? '已用' : '剩余'
// 竞赛不需要
if (!this.competitionId) {
this.countVal = isPrac ? 0 : (new Date(this.endTime).getTime() - Date.now()) / 1000 // 如果是考核,取考核的结束时间减去当前时间去做倒计时,练习则直接给0做计时
this.startCount()
}
3 years ago
resolve()
}).catch(err => {
reject()
4 years ago
})
3 years ago
})
4 years ago
},
// 关闭父页面的loading,并置加载完成状态为true
2 years ago
closeLoad () {
this.$parent.loadIns.close()
this.$parent.loaded = true
},
2 years ago
// 设置isSubmit
setSubmit (status) {
this.isSubmit = status
newmain.$emit('isSubmit', status)
Cookie.set('admin-isSubmit', status)
},
// 获取考核列表来查询该考核是否已经考过
2 years ago
getAssList () {
this.$post(`${this.api.pageStuAssessment}`, {
pageNum: 1,
pageSize: 10000
}).then(res => {
const list = res.list
2 years ago
const { assessmentId } = this
let done = false
const classId = this.classId
2 years ago
// 匹配到该考核,并且已经提交过(有reportId则说明有实验记录),并且classId跟当前用户的classId相同,就提示提交了考核然后返回上一页
if (list.find(e => e.assessmentId == assessmentId && e.reportId && e.classId == classId)) {
done = true
2 years ago
this.setSubmit(true)
this.$message.error('你已经提交过该考核!')
setTimeout(_ => {
history.back() // 直接返回上一页面
}, 1500)
}
// 如果该考核没有提交过,则取获取项目列表进行考核
if (!done) {
this.getList().then(() => {
this.getCache()
2 years ago
}).catch(res => { })
// 查询考核状态定时器
this.statusTimer = setInterval(_ => {
this.getAssStatus()
}, 1000)
}
2 years ago
}).catch(res => { })
},
// 获取上次缓存记录
2 years ago
getCache (cache) {
const pId = cache ? cache.projectId : ''
const projectId = Number(pId || this.projectId)
const cid = this.courseId
const assessmentId = this.assessmentId
const list = this.projectList
let points = []
3 years ago
if (pId && cache.judgmentIdList && !Cookie.get('admin-projectId')) {
cache.judgmentIdList.map(e => {
points.push({
judgmentId: e
})
})
} else {
// 深拷贝判分列表,下面接口获取到有缓存代码就直接存进去,如果用户选择了恢复代码,再用这个列表替换原来的判分列表
points = JSON.parse(JSON.stringify(this.points))
}
// 如果是没有代码的缓存,则只需要恢复项目,否则,就调接口查询缓存代码
3 years ago
if (cache && cache.empty && list.find(e => e.projectId === projectId) && !Cookie.get('admin-projectId')) {
if (Cookie.get('admin-projectId') && !this.competitionId) {
3 years ago
Cookie.remove('admin-projectId')
} else {
this.projectId = projectId
this.getProDetail()
}
this.closeLoad()
localStorage.removeItem('codeCache')
} else {
if (Cookie.get('admin-projectId') && !this.competitionId) {
3 years ago
Cookie.remove('admin-projectId')
}
let newJudgmentId = '' // 要恢复到第一个有代码的判分规则,因为有代码的规则可能不是第一个,所以要判断如果这个为空,才把索引赋给该变量
const promiseList = [] // promise数组
let hasCache = 0 // 是否有缓存
points.map((e, i) => {
const judgmentId = e.judgmentId
2 years ago
promiseList.push(new Promise((resolve, reject) => {
this.$post(this.api.getLastCache, {
assessmentId: assessmentId ? Number(assessmentId) : '',
bcId: judgmentId,
projectId, // 项目id,同上
cid // 课程id
}).then(res => {
this.closeLoad()
const code = res.getLastCache
// 如果有缓存代码
if (code) {
hasCache = 1
if (newJudgmentId === '') newJudgmentId = i
e.code = code
}
resolve()
}).catch(res => {
reject()
this.closeLoad()
})
}))
})
// 如果有缓存代码,再提示用户是否要继续上次的实验
Promise.all(promiseList).then(_ => {
hasCache && this.$confirm('是否要继续上次的实验?', '提示', {
confirmButtonText: '是',
cancelButtonText: '否',
type: 'success'
}).then(() => {
localStorage.removeItem('codeCache') // 恢复代码后清除本地缓存
this.projectId = projectId
// 如果是本地缓存里有项目id,则要再次获取项目详情,取判分列表再次赋值;本地没有缓存则取只恢复第一个判分点的代码
if (pId) {
this.getProDetail().then(() => {
this.points.map(e => {
const item = points.find(n => n.judgmentId === e.judgmentId)
if (item && item.code) this.$set(e, 'code', item.code)
})
this.$emit('tell', projectId, this.curSystemId, this.points)
this.$emit('recoveryCode', newJudgmentId + '') // 切换为缓存的判分点,tab索引值要为字符串
2 years ago
}).catch(res => { })
} else {
this.points.map(e => {
const item = points.find(n => n.judgmentId === e.judgmentId)
if (item && item.code) this.$set(e, 'code', item.code)
})
this.$emit('tell', projectId, this.curSystemId, this.points)
this.$emit('recoveryCode')
}
}).catch(() => {
// 选择了不继续上次的实验,则清除本地缓存,并删除服务器里缓存的代码
localStorage.removeItem('codeCache')
// 删除该项目下所有判分规则的缓存代码
points.map(e => {
e.code && this.$post(this.api.delCache, {
assessmentId,
bcId: e.judgmentId,
projectId,
cid
2 years ago
}).then(res => { }).catch(() => { })
})
3 years ago
})
})
}
4 years ago
},
// 定时查询考核状态(查到考核如果结束后,直接提交考核)
2 years ago
getAssStatus () {
// 未提交才需要查询状态
this.isSubmit || this.$get(this.api.getDetailById, {
id: this.assessmentId
}).then(res => {
const done = res.data ? res.data.status === 2 : false // 状态(0、待开始 1、进行中 2、已结束)
// 如果考核已结束,则清除查询考核状态的定时器,并且自动提交
if (done) {
clearInterval(this.statusTimer)
3 years ago
this.$alert('考核时间已到,系统已自动交卷', '提示', {
confirmButtonText: '确定'
})
this.submit()
}
2 years ago
}).catch(res => { })
},
// 定时查询竞赛状态(查到竞赛如果结束后,直接提交竞赛)
2 years ago
getCompetitionStatus () {
// 未提交才需要查询状态
this.isSubmit || this.$post(`${this.api.getCompetition}?competitionId=${this.competitionId}`).then(({ competition }) => {
const stages = competition.competitionStage
if (stages) {
const stage = stages.find(e => e.stageId == this.stageId)
const endTime = new Date(stage.endTime).getTime()
const now = Date.now()
// 如果已经结束
if (now >= new Date(stage.endTime)) {
clearInterval(this.statusTimer)
this.$alert('竞赛时间已到,系统已自动交卷', '提示', {
confirmButtonText: '确定'
})
this.submit()
} else { // 没结束,则显示倒计时(竞赛才需要通过定时调接口来显示倒计时,因为中台可以修改结束时间,所以需要时刻获取最新的结束时间)
this.counter((endTime - now) / 1000)
}
}
2 years ago
}).catch(res => { })
},
3 years ago
// 项目选择回调
2 years ago
selectProject () {
4 years ago
this.isSelected = true
this.getProDetail().then(() => {
this.getCache()
2 years ago
}).catch(res => { })
2 years ago
this.setSubmit(false)
4 years ago
this.countVal = 0
this.grade = '00'
3 years ago
this.pannelTab = 'first'
2 years ago
this.clearReport()
this.$emit('recoveryCode') // 切换实训项目后回到第一个判分点
},
2 years ago
// 切换实验报告输入
toggleReport () {
this.reportVisible = !this.reportVisible
},
// 清空实验报告填写里的内容
clearReport () {
this.taskList.forEach(e => {
e.code = ''
e.codeResult = ''
})
},
// 查看实验报告
2 years ago
toReport () {
2 years ago
Cookie.set('doneProjectId', this.projectId) // 进入实验报告之前先存储当前项目id,从实验报告返回来后把这个id恢复
2 years ago
const { reportId } = this.$store.state
// 老师端进来的,直接跳转到实验报告,学生端进来的,跳转到实验记录里的实验报告
if (this.fromManager) {
this.$router.push(`/report?reportId=${reportId}`)
} else {
let href = location.origin
2 years ago
if (this.$config.isTest) href += '/student'
2 years ago
href += `/#/record/show?python=1&reportId=${reportId}`
// 练习
if (this.projectPermissions == 0) {
href += `&curriculumId=${this.courseId}&projectId=${this.projectId}`
}
location.href = href
}
},
//重新开始
2 years ago
reload () {
this.reloadCount()
this.grade = '00'
localStorage.removeItem('codeCache')
2 years ago
this.setSubmit(false)
3 years ago
const points = this.points
// code和codeId,还有运行结果全部清空
3 years ago
points.map(e => {
e.code = ''
e.codeId = ''
e.answer = ''
e.retResult = ''
3 years ago
})
this.$emit('tell', this.projectId, this.curSystemId, points)
this.$emit('recoveryCode')
this.startCount()
2 years ago
this.clearReport()
},
// 页面加载完后重置编辑框大小
ready () {
for (let i = 0; i < this.points.length; i++) {
const code = this.$refs['codemirror' + i][0].codemirror
console.log('ready', code)
// code && code.setSize('auto', 'calc(100vh - 150px)')
code && code.setSize('auto', '300px')
}
},
// 提交询问
2 years ago
confirmSubmit () {
2 years ago
const isVscode = this.$parent.language // 是否选择了vscode
const { taskList } = this
const pointList = this.$parent.workbench
3 years ago
let msg = '此操作将视为结束考试,是否继续?'
1 year ago
if (!isVscode) {
// 先判断是否有输了代码没有跑的情况,如果有,弹出该提示;
if (pointList.find(e => !e.codeId && e.code)) msg = '有代码没有运行,该代码将不得分,确定提交?'
// 再判断每个任务下是否有没有输代码的情况,如果有,弹出该提示
if (pointList.find(e => !e.codeId && !e.code)) msg = '任务未完成,是否直接提交?'
} else {
if (!taskList.find(e => e.code || e.codeResult)) msg = '实验报告未填写,实验成绩为零,是否确认提交?'
}
3 years ago
this.$confirm(msg, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
center: true
}).then(() => {
2 years ago
// vscode类型的产品提交前需要自动运行每个判分点的代码以获取codeId
if (isVscode) {
const promises = []
taskList.map(async (e, i) => {
(e.code || e.codeResult) && promises.push(new Promise(async (resolve, reject) => {
const { code, codeId } = await this.$post(this.api.runPythonCode, {
code: e.code,
bcId: e.judgmentId,
cid: this.courseId,
projectId: this.projectId,
userAnswer: e.codeResult || null,
type: 1
})
this.$parent.workbench[i].codeId = codeId
this.$parent.workbench[i].answer = e.codeResult
this.$parent.workbench[i].retResult = 1
resolve()
}))
})
Promise.all(promises).then(_ => {
3 years ago
this.submit()
2 years ago
})
3 years ago
} else {
2 years ago
// 如果全部都没运行直接点提交,则主动运行一个空代码。(不然会造成上次运行的结果,这次进来不运行直接提交的话,会无法取到运行结果)
if (!pointList.find(e => e.codeId)) {
this.$post(this.api.runPythonCode, {
code: '',
bcId: pointList[0].judgmentId,
cid: this.courseId,
projectId: this.projectId,
type: 0
}).then(({ codeId }) => {
this.$parent.workbench[0].codeId = codeId
this.submit()
}).catch(err => { })
} else {
this.submit()
}
3 years ago
}
2 years ago
}).catch(() => { })
},
// 提交
2 years ago
submit () {
if (this.isSubmit) return false
const pointList = this.$parent.workbench
const date = new Date()
const entryTime = this.entryTime
const timeSum = Math.ceil((date.getTime() - entryTime.getTime()) / 60000) // 计算实验用时(分钟),向上取整
const submitTime = util.formatDate('yyyy-MM-dd hh:mm:ss', date)
const projectId = this.projectId
const pro = this.projectList.find(e => e.projectId == projectId)
const projectName = pro ? pro.projectName : '' // 获取项目名称
this.reloadCount()
// 判分点参数
const attributesReqList = []
pointList.map(e => {
attributesReqList.push({
codeId: e.codeId,
bcId: e.judgmentId,
isSubmit: e.codeId ? 1 : 0,
answer: e.answer,
retResult: e.retResult
})
})
const data = {
classId: this.classId ? this.classId : '',
className: this.className ? this.className : '',
curriculumId: this.courseId,
startTime: this.projectPermissions ? this.startTime : util.formatDate('yyyy-MM-dd hh:mm:ss', entryTime), // 开始时间(考核:直接从职站取考核的开始时间;练习:取页面进入的时间)
endTime: this.projectPermissions ? this.endTime : submitTime, // 结束时间(考核:直接从职站取考核的结束时间;练习:取提交时间)
submitTime, // 提交时间,即当前时间(这3个时间都是传完整的日期时间格式)
timeSum,
projectId,
projectName,
assessmentId: this.assessmentId ? this.assessmentId : '',
totalScore: 100, // 判分点总分固定为100
systemId: this.curSystemId,
purpose: this.pd.experimentTarget, // 实验目的
attributesReqList,
competitionId: this.competitionId,
stageId: this.stageId,
teamId: this.teamId,
2 years ago
mallId: this.mallId
}
this.$post(this.api.submit, data).then(({ retInfo, reportId }) => {
localStorage.removeItem('codeCache')
2 years ago
this.setSubmit(true)
clearInterval(this.statusTimer)
2 years ago
this.reportVisible = false
let list = retInfo
2 years ago
let { taskList } = this
let score = 0
// 给判分列表添加分数和运行结果
taskList.map(e => {
let item = list.find(n => n.judgmentPointsId === e.judgmentId)
if (item) {
e.examScore = item.score
e.finishedResult = item.finishedResult // 1:正确,2:错误
} else {
3 years ago
e.examScore = 0
}
3 years ago
score += e.examScore // 计算总分
})
this.grade = util.handleZero(score) // 前置加0(竞赛不显示分数)
this.reportId = reportId
2 years ago
this.$store.commit('setReportId', reportId)
2 years ago
this.$store.commit('setTaskList', taskList)
this.editReport(reportId)
// 如果是竞赛,并且勾选了公布成绩详情的选项,则弹框提示
this.competitionId && this.resultsDetails == 0 && this.$alert(`提交成功${this.resultAnnouncementTime != 0 ? ',成绩将在' + this.resultAnnouncementTime + '小时后发布,请去参赛信息模块查看' : ''}`, '提示', {
confirmButtonText: '确定',
callback: action => {
this.$parent.back()
}
})
2 years ago
}).catch(err => { })
},
3 years ago
// 编辑实验报告
2 years ago
editReport (reportId) {
3 years ago
const data = []
const ans = []
const promises = []
this.taskList.map(e => {
promises.push(new Promise((resolve, reject) => {
data.push({
judgmentId: e.judgmentId,
judgmentName: e.name,
answer: e.code,
score: e.examScore,
2 years ago
quesScore: e.score,
3 years ago
})
// 获取参考答案
this.$get(this.api.queryBcJudgmentPointByBcId, {
bcId: e.judgmentId
}).then(({ judgmentPoint }) => {
ans.push(judgmentPoint)
resolve()
}).catch(err => {
reject()
})
}))
})
Promise.all(promises).then(_ => {
data.map(e => {
const item = ans.find(n => n.bcId === e.judgmentId)
e.referenceAnswer = item.experimentCode
})
this.$post(this.api.editExperimentalData, {
reportId,
data: JSON.stringify(data)
2 years ago
}).then(res => { }).catch(err => { })
3 years ago
})
},
// 实验面板显示隐藏
2 years ago
togglePannel () {
2 years ago
document.querySelector('#panel').style.left = 0
this.pannelVisible = !this.pannelVisible
2 years ago
// if (this.pannelVisible) {
// document.querySelector('#toggle').onmousedown = null
// document.querySelector('#toggle').onmousedown = togglePannel
// } else {
// this.drag()
2 years ago
// }
},
// 倒计时
2 years ago
timeFormat (param) {
return param < 10 ? '0' + param : param
},
// 清除时间
2 years ago
reloadCount () {
clearInterval(this.counterTimer)
this.countVal = ''
this.day = '00'
this.seconds = '00'
this.minutes = '00'
this.hour = '00'
4 years ago
},
// 计时器(考核是倒计时,练习是计时)
2 years ago
counter (counterTime) {
let leave1 = counterTime % (24 * 3600) //计算天数后剩余的毫秒数
let leave2 = leave1 % 3600 //计算小时数后剩余的毫秒数
let leave3 = leave2 % 60 //计算分钟数后剩余的毫秒数
3 years ago
let day = Math.floor(counterTime / (24 * 3600)) //计算相差天数
let hour = Math.floor(leave1 / 3600) //计算相差小时
let minutes = Math.floor(leave2 / 60) //计算相差分钟
let seconds = Math.round(leave3) //计算相差秒
day = this.timeFormat(day)
hour = this.timeFormat(hour)
minutes = this.timeFormat(minutes)
seconds = this.timeFormat(seconds)
this.day = day
this.hour = hour
this.minutes = minutes
this.seconds = seconds
4 years ago
},
// 启动倒计时
2 years ago
startCount () {
clearInterval(this.counterTimer)
this.counterTimer = setInterval(() => {
this.counter(this.projectPermissions ? this.countVal-- : this.countVal++)
}, 1000)
2 years ago
},
// 拖拽面板
2 years ago
drag () {
2 years ago
document.querySelector('.scrollbar').onmousedown = null
document.querySelector('#toggle').onmousedown = null
2 years ago
const el = document.querySelector('#panel')
const drag = e => {
2 years ago
if (this.dragIds.includes(e.target.id)) {
e.stopPropagation && e.stopPropagation()
e.preventDefault && e.preventDefault()
e.cancelBubble = true
e.returnValue = false
let x = e.clientX - el.offsetLeft
let y = e.clientY - el.offsetTop
let left = 0
let top = 0
2 years ago
2 years ago
document.onmousemove = function (eve) {
left = eve.clientX - x
top = eve.clientY - y
el.style.left = left + 'px'
el.style.top = top + 'px'
}
document.onmouseup = e => {
document.onmousemove = null
document.onmouseup = null
}
}
}
2 years ago
document.querySelector('.scrollbar').onmousedown = drag
const drag1 = e => {
e.stopPropagation && e.stopPropagation()
e.preventDefault && e.preventDefault()
e.cancelBubble = true
e.returnValue = false
let timer = setTimeout(this.togglePannel, 200)
let x = e.clientX - el.offsetLeft
let y = e.clientY - el.offsetTop
let left = 0
let top = 0
document.onmousemove = function (eve) {
clearTimeout(timer)
2 years ago
left = eve.clientX - x
top = eve.clientY - y
el.style.left = left + 'px'
el.style.top = top + 'px'
}
document.onmouseup = e => {
document.onmousemove = null
document.onmouseup = null
}
}
2 years ago
document.querySelector('#toggle').onmousedown = this.pannelVisible ? this.togglePannel : drag1
4 years ago
}
}
};
</script>
<style lang="scss" scoped>
.el-main {
width: 60%;
background-color: #fff;
color: #333;
padding: 0;
font-size: 16px;
margin: 0px 20px 10px 10px;
overflow: hidden;
4 years ago
}
3 years ago
.panel-header {
display: flex;
justify-content: space-between;
align-items: center;
.project {
display: inline-flex;
align-items: center;
width: 28%;
}
.item {
font-size: 16px;
margin: 0 10px;
padding: 20px 0;
}
.count {
margin-left: -40px;
span {
padding: 5px 15px;
margin: 0 5px;
color: #333;
font-size: 14px;
text-align: center;
background: #fff;
border-radius: 18px;
}
}
.total-score {
padding: 10px;
font-size: 14px;
text-align: center;
border-radius: 6px;
}
.submit {
3 years ago
width: 106px;
3 years ago
font-size: 16px;
}
.reload {
color: #d0d0d0;
3 years ago
font-size: 16px;
3 years ago
background-color: #202020;
}
}
/deep/.des {
3 years ago
font-size: 16px;
2 years ago
font-family: 'Microsoft YaHei';
img {
max-width: 100%;
}
3 years ago
}
1 year ago
/deep/.ql-editor {
font-size: 13px;
}
3 years ago
/deep/.el-collapse-item__wrap {
4 years ago
border-bottom: none;
}
3 years ago
/deep/.el-collapse-item__header {
4 years ago
border-bottom: none;
}
3 years ago
/deep/.el-icon-s-ticket:before {
4 years ago
padding: 5px;
font-size: 16px;
4 years ago
}
3 years ago
/deep/.el-collapse-item__arrow {
4 years ago
margin: 0 5px 0 0;
}
2 years ago
/deep/.info-tab.el-tabs--card {
.el-tabs__item {
font-size: 16px;
}
.el-tabs__item.is-active {
color: #fff;
}
.el-tabs__header .el-tabs__nav {
border: none;
}
.el-tabs__header .el-tabs__item {
border-left: none;
}
.el-tabs__header {
padding: 5px 20px;
border-bottom: none;
}
& > .el-tabs__content {
margin: 0 20px;
max-height: calc(60vh - 70px);
overflow: auto;
}
4 years ago
}
3 years ago
/deep/.el-collapse {
4 years ago
border-bottom: none;
border-top: none;
}
.el-aside {
3 years ago
margin-bottom: 10px;
4 years ago
color: #333;
3 years ago
background-color: #fff;
4 years ago
}
2 years ago
.el-aside /deep/[class*=' el-icon-'],
[class^='el-icon-'] {
4 years ago
line-height: 40px;
font-size: 16px;
}
.aside-header {
margin: 0px 10px 10px 10px;
background-color: #fff;
}
.aside-footer {
margin: 0px 10px 10px 10px;
background-color: #fff;
}
.p-title {
display: flex;
4 years ago
justify-content: center;
height: 40px;
3 years ago
&.system4 {
background-size: 100% 58px;
}
2 years ago
&.system7,
&.system9 {
background-size: 100% 40px;
3 years ago
}
2 years ago
&.system8,
&.system6 {
3 years ago
background-size: 100% 61px;
}
p {
padding-left: 10px;
line-height: 40px;
font-size: 16px;
color: #fff;
}
i {
color: #fff;
}
4 years ago
}
3 years ago
/deep/.el-card__body {
4 years ago
padding: 0;
}
2 years ago
/deep/.task-table {
4 years ago
font-size: 12px;
3 years ago
thead {
color: #fff;
font-size: 10px;
}
3 years ago
th > .cell {
font-weight: 100;
}
td,
th.is-leaf {
border-bottom: 0 !important;
}
3 years ago
.el-table__cell {
padding: 6px 0;
}
4 years ago
}
3 years ago
.goal {
padding: 10px 0;
4 years ago
margin: 0 10px;
font-size: 14px;
}
/deep/.select {
.el-select__caret:before {
2 years ago
content: '\e78f';
padding: 3px;
font-size: 16px;
color: #fff;
border-radius: 50%;
}
.el-input__icon {
line-height: 60px;
}
.el-input {
padding: 10px 0;
}
.el-input--suffix .el-input__inner {
height: 40px !important;
3 years ago
padding-right: 50px;
margin-left: 15px;
color: #333;
font-size: 14px;
border-radius: 30px;
border: none;
background-color: #fff;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
4 years ago
}
2 years ago
.panel {
2 years ago
z-index: 1000;
2 years ago
position: relative;
top: 200px;
bottom: 20px;
left: 0;
width: 0;
2 years ago
height: 0;
.toggle-panel {
position: absolute;
top: 60%;
&.active {
left: 100%;
}
img {
height: 150px;
}
}
&.active {
position: fixed;
width: 85%;
2 years ago
height: 70%;
.toggle-panel {
top: 38%;
}
}
}
/deep/.el-container {
2 years ago
height: 100%;
&.is-vertical {
background-color: #f5f5f5;
}
4 years ago
}
3 years ago
.right {
color: #00af00;
font-size: 20px;
}
.wrong {
color: #f00;
font-size: 20px;
}
.info {
color: #bfbfbf;
cursor: pointer;
&:hover {
2 years ago
opacity: 0.9;
}
}
2 years ago
/deep/.report-table.el-table {
th {
background-color: #c6daff !important;
}
}
.report-wrap {
position: relative;
padding-top: 5px;
}
.close-report {
z-index: 2;
position: absolute;
top: -21px;
right: 296px;
font-size: 32px;
color: #c6daff;
cursor: pointer;
}
/deep/.v-note-wrapper {
min-height: 0;
height: auto;
2 years ago
.v-note-panel .v-note-show {
overflow: visible;
.v-show-content {
overflow: visible;
}
}
}
4 years ago
</style>