试卷预览等

master
yujialong 3 months ago
parent 6403d33eaf
commit 1604ff8684
  1. 2
      src/App.vue
  2. 1
      src/api/index.js
  3. 1
      src/assets/images/exit.svg
  4. BIN
      src/assets/images/group.png
  5. BIN
      src/assets/images/logo1.png
  6. BIN
      src/assets/images/person/bg.png
  7. 1
      src/assets/images/shrink.svg
  8. 1
      src/assets/images/tag-active.svg
  9. 1
      src/assets/images/tag.svg
  10. 1
      src/layouts/header/index.vue
  11. 19
      src/layouts/home/index.vue
  12. 10
      src/layouts/navbar/index.vue
  13. 14
      src/libs/util.js
  14. 7
      src/pages/ques/detail/index.vue
  15. 6
      src/pages/testPaper/detail/index.vue
  16. 45
      src/pages/testPaper/detail/template.vue
  17. 600
      src/pages/testPaper/preview/index.vue
  18. 5
      src/router/modules/testPaper.js
  19. 17
      src/store/modules/testPaper.js
  20. 5
      src/styles/common.scss

@ -1,6 +1,6 @@
<template>
<div id="app">
<el-radio-group v-if="Setting.isDev" v-model="ip" @change="ipChange">
<el-radio-group v-if="Setting.isDev" class="ip" v-model="ip" @change="ipChange">
<el-radio :label="0">刘榕ip</el-radio>
<el-radio :label="1">陈赓ip</el-radio>
<el-radio :label="2">测试服ip</el-radio>

@ -2,6 +2,7 @@ import Setting from '@/setting'
const { apiBaseURL: host } = Setting
export default {
getCurrentTime: `/competition/competition/management/getCurrentTime`,
encrypt: `/nakadai/data/encrypt`,
queryProfessional: `/exam/exam/professional/queryProfessional`,

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1723618392232" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4281" width="30" height="30" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M511.771963 1023.291077c-63.724308 0-125.558154-12.445538-183.768615-37.100308a469.464615 469.464615 0 0 1-150.134154-101.139692 471.118769 471.118769 0 0 1 59.392-717.351385 37.021538 37.021538 0 0 1 43.008 59.864616 403.692308 403.692308 0 0 0-121.304616 139.027692A391.798154 391.798154 0 0 0 113.435963 551.227077c0 106.338462 41.432615 206.375385 116.657231 281.6a395.736615 395.736615 0 0 0 281.678769 116.736c106.338462 0 206.454154-41.432615 281.757539-116.736a395.421538 395.421538 0 0 0 116.65723-281.6 391.483077 391.483077 0 0 0-45.528615-184.635077 403.298462 403.298462 0 0 0-121.304615-138.870154 36.864 36.864 0 0 1 43.008-59.943384 471.04 471.04 0 0 1 59.392 717.430153 471.04 471.04 0 0 1-333.981539 138.161231z m11.106462-512.236308a36.864 36.864 0 0 1-36.94277-36.864V37.021538a37.021538 37.021538 0 0 1 73.964308 0v437.169231c0 20.48-16.541538 36.864-37.021538 36.864z" fill="#ffffff" p-id="4282"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1723535697263" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5903" width="18" height="18" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M877.5 565.9l-367.6-340-367.6 340c-22.5 14.5-52.5 14.5-67.5 0-15-21.7-15-50.7 0-65.1L495 117.5c0-7.2 7.5-7.2 15-7.2s7.5 0 15 7.2l420.2 383.3c22.5 21.7 15 50.6 0 65.1-22.7 14.5-52.7 14.5-67.7 0zM494.8 450.1c0-7.2 7.5-7.2 15-7.2s15 0 15 7.2L945 833.4c22.5 21.7 15 50.6 0 65.1-22.5 14.4-52.5 14.4-67.5 0L509.8 558.6 142.2 905.8c-22.5 14.5-52.5 14.5-67.5 0-15-21.7-15-50.7 0-65.1l420.1-390.6z m0 0" fill="#666666" p-id="5904"></path></svg>

After

Width:  |  Height:  |  Size: 766 B

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1723619200843" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7128" width="18" height="18" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M695.7 118.6H326.9c-58.2 0-105.4 47.2-105.4 105.4v579.6c0 58.2 47.2 105.4 105.4 105.4l184.4-184.4L695.7 909c58.2 0 105.4-47.2 105.4-105.4V224c0-58.2-47.1-105.4-105.4-105.4z" fill="#d81e06" p-id="7129"></path></svg>

After

Width:  |  Height:  |  Size: 545 B

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1723619200843" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7128" width="18" height="18" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M695.7 118.6H326.9c-58.2 0-105.4 47.2-105.4 105.4v579.6c0 58.2 47.2 105.4 105.4 105.4l184.4-184.4L695.7 909c58.2 0 105.4-47.2 105.4-105.4V224c0-58.2-47.1-105.4-105.4-105.4z" fill="#bfbfbf" p-id="7129"></path></svg>

After

Width:  |  Height:  |  Size: 545 B

@ -1,7 +1,6 @@
<template>
<div class="header">
<div class="group">
<img src="@/assets/images/group.png">
<breadcrumb v-if="crumbRefresh" ref="breadcrumb" :data="crumbs"></breadcrumb>
</div>
<div class="user-tool">

@ -1,7 +1,7 @@
<template>
<div class="main">
<v-navbar class="nav"></v-navbar>
<div class="layout">
<v-navbar v-if="!hideNav" class="nav" />
<div :class="['layout', { full: hideNav }]">
<div class="content">
<transition name="move" mode="out-in">
<router-view class="view"></router-view>
@ -21,7 +21,8 @@ import vFooter from '../footer'
export default {
data () {
return {
hideNavPath: ['/testPaper/preview'],
hideNav: true,
};
},
components: {
@ -32,6 +33,9 @@ export default {
mounted () {
const { token } = this.$route.query
token && Util.local.set(Setting.tokenKey, token, Setting.tokenExpires)
const { path } = this.$route
this.hideNav = this.hideNavPath.includes(path)
},
};
</script>
@ -49,6 +53,7 @@ export default {
.layout {
width: calc(100% - 256px);
.content {
height: 100vh;
padding-left: 24px;
@ -58,6 +63,14 @@ export default {
min-height: 100%;
}
}
&.full {
width: 100%;
.content {
padding-left: 0;
}
}
}
}
</style>

@ -47,11 +47,11 @@ export default {
siteActive: '/column',
menus: [],
defaultMenus: [
{
icon: 'site',
index: '/site/list',
title: '首页'
},
// {
// icon: 'site',
// index: '/site/list',
// title: ''
// },
{
icon: 'user',
index: 'user',

@ -1,6 +1,9 @@
import { _local } from "./util.db";
import { Message } from "element-ui";
import store from "@/store";
import Setting from '@/setting'
import axios from 'axios'
import api from '@/api'
// 文件后缀集合
const exts = {
@ -156,6 +159,17 @@ const util = {
getFileExt (fileName) {
return fileName.substring(fileName.lastIndexOf(".") + 1);
},
// 获取当前时间
getNow () {
return new Promise(async (resolve, reject) => {
const { data } = await axios.get(Setting.apiBaseURL + api.getCurrentTime, {
headers: {
token: _local.get(Setting.tokenKey)
}
})
resolve(new Date(data.currentTime))
})
},
};
export default util;

@ -578,8 +578,13 @@ export default {
const blanks = this.fillBlanks
blanks.map((e, i) => e.serial = i + 1) //
form.questionAnswerVersions[0].answerData = JSON.stringify(blanks)
} else if (form.questionType === 'essay' && this.$refs.referenceAnswer) {
form.questionAnswerVersions[0].referenceAnswer = this.$refs.referenceAnswer.getUEContent() // ()
} else {
form.questionAnswerVersions.map((e, i) => {
e.optionNumber = i + 1
})
}
if (form.questionType === 'essay' && this.$refs.referenceAnswer) form.questionAnswerVersions[0].referenceAnswer = this.$refs.referenceAnswer.getUEContent() // ()
// debugger
this.tempForm = form
this.keep = keep

@ -177,7 +177,7 @@
<div class="btns">
<el-button @click="submit(1)">保存草稿</el-button>
<el-button type="primary" @click="submit(0)">发布</el-button>
<el-button v-if="$route.query.id" @click="preview">预览</el-button>
<el-button v-if="paperId" @click="preview">预览</el-button>
<el-button @click="back">取消</el-button>
</div>
@ -872,6 +872,10 @@ export default {
// if (this.hasRepeatQues(allQues)) return false
this.saveTestPaper()
},
//
preview () {
window.open(this.$router.resolve(`preview?id=${this.paperId}`).href)
},
//
back () {
this.$router.back()

@ -7,7 +7,7 @@
<li>
<label>题型</label>
<el-select v-model="filter.questionType" clearable placeholder="请选择题目类型" @change="initData">
<el-option v-for="(item, i) in questionTypes" :key="i" :value="item.name"></el-option>
<el-option v-for="(item, i) in questionTypes" :key="i" :value="item.id" :label="item.name"></el-option>
</el-select>
</li>
<li>
@ -24,7 +24,7 @@
<el-table :data="list" stripe header-align="center" row-key="libraryId">
<el-table-column type="index" width="60" label="序号" align="center"></el-table-column>
<el-table-column prop="templateName" label="模板名称" align="center" min-width="100"></el-table-column>
<el-table-column prop="questionType" label="题型" align="center" min-width="140"></el-table-column>
<el-table-column prop="questionTypeName" label="题型" align="center" min-width="140"></el-table-column>
<el-table-column prop="outlineNum" label="大题数" align="center" width="60"></el-table-column>
<el-table-column prop="questionNum" label="小题总数" align="center" width="80"></el-table-column>
<el-table-column prop="totalScore" label="总分" align="center" width="60"></el-table-column>
@ -95,30 +95,15 @@
import Setting from '@/setting'
import Util from '@/libs/util'
import _ from 'lodash'
import QuesConst from '@/const/ques'
export default {
props: ['visible'],
data () {
return {
arabicToChinese: Util.arabicToChinese,
questionTypes: QuesConst.questionTypes,
listVisible: false,
searchTimer: null,
questionTypes: [
{
name: '单选题'
},
{
name: '多选题'
},
{
name: '判断题'
},
{
name: '填空题'
},
{
name: '问答题'
},
],
filter: {
templateName: '',
@ -138,35 +123,35 @@ export default {
examQuestions: [],
outlineName: '单选题',
questionNum: '',
questionType: '单选题',
questionType: 'single_choice',
targetScore: '',
},
{
examQuestions: [],
outlineName: '多选题',
questionNum: '',
questionType: '多选题',
questionType: 'multiple_choice',
targetScore: '',
},
{
examQuestions: [],
outlineName: '判断题',
questionNum: '',
questionType: '判断题',
questionType: 'judgement',
targetScore: '',
},
{
examQuestions: [],
outlineName: '填空题',
questionNum: '',
questionType: '填空题',
questionType: 'fill_blank',
targetScore: '',
},
{
examQuestions: [],
outlineName: '问答题',
questionNum: '',
questionType: '问答题',
questionType: 'essay',
targetScore: '',
},
],
@ -210,7 +195,17 @@ export default {
pageSize: this.pageSize,
...this.filter
})
this.list = res.pageList.records
const list = res.pageList.records
const types = QuesConst.questionTypes
list.map(e => {
const names = []
e.questionType.split('、').map(n => {
const item = types.find(m => m.id === n)
item && names.push(item.name)
})
if (names.length) e.questionTypeName = names.join('、')
})
this.list = list
this.total = res.pageList.total
} catch (e) { }
},

@ -0,0 +1,600 @@
<template>
<div class="index">
<div class="top">
<p class="names">{{ form.name }}</p>
<div class="count m-r-30">
用时
<span>{{ timeSum.day }}</span>
<span>{{ timeSum.hour }}</span>小时
<span>{{ timeSum.minutes }}</span>
<span>{{ timeSum.seconds }}</span>
</div>
<img class="exit" src="@/assets/images/exit.svg" alt="" @click="close">
</div>
<div class="wrap">
<div class="left">
<h6 class="title">答题卡</h6>
<div class="progress">
<p class="fs-14">答题进度</p>
<el-progress class="m-t-5 m-b-5" :percentage="progress" :format="progressFormat"></el-progress>
<p>{{ form.questionCount }}满分{{ form.score }}</p>
</div>
<div v-if="form.paperOutline" class="type-wrap">
<div v-for="(item, i) in form.paperOutline" :key="i" class="type">
<h6 class="stem">{{ arabicToChinese(i + 1) }}{{ item.questionTypeName }}本题共{{ item.questionNum }}小题{{
item.targetScore }}</h6>
<ul class="serials">
<template v-for="(ques, j) in item.examQuestions">
<li
v-if="!sheetStatus || (sheetStatus === 1 && !ques.answered) || (sheetStatus === 2 && ques.answered) || (sheetStatus === 3 && ques.partAnswer) || (sheetStatus === 4 && ques.sign)"
:key="j" :class="{ answered: ques.answered, partAnswer: ques.partAnswer }">
<img v-if="ques.sign" class="tag" src="@/assets/images/tag-active.svg" alt="">
{{ j + 1 }}
</li>
</template>
</ul>
</div>
</div>
<ul class="status-filter">
<li :class="{ active: sheetStatus === 1 }" @click="filterStatus(1)">未作答</li>
<li :class="{ active: sheetStatus === 2 }" @click="filterStatus(2)">已作答</li>
<li :class="{ active: sheetStatus === 3 }" @click="filterStatus(3)">部分已作答</li>
<li :class="{ active: sheetStatus === 4 }" @click="filterStatus(4)"><img class="tag"
src="@/assets/images/tag-active.svg" alt=""> 已标记</li>
</ul>
</div>
<ul v-if="form.paperOutline" class="ques-wrap">
<li v-for="(item, i) in form.paperOutline" :key="i">
<div class="outline">
{{ arabicToChinese(i + 1) }}{{ item.questionTypeName }}本题共{{ item.questionNum }}小题{{
item.targetScore }}
<img :class="['shrink', { active: item.shrink }]" src="@/assets/images/shrink.svg" alt=""
@click="item.shrink = !item.shrink">
</div>
<div :class="['ques', { hide: item.shrink }]">
<div v-for="(ques, j) in item.examQuestions" :key="j" class="item">
<div class="stem-wrap">
<span class="label">{{ j + 1 }} / {{ item.questionNum }}</span>
<span class="label">{{ item.questionTypeName }}</span>
<div :id="'stem' + ques.id" v-html="getQuesStem(ques)"></div>
<p v-if="item.questionType !== 'fill_blank'">{{ ques.score }}</p>
<img class="tag" :src="require('@/assets/images/' + (ques.sign ? 'tag-active' : 'tag') + '.svg')" alt=""
@click="ques.sign = ques.sign ? 0 : 1">
</div>
<!-- 单选多选判断的选项 -->
<template
v-if="item.questionType !== 'fill_blank' && item.questionType !== 'essay' && ques.questionAnswerVersionsList">
<div v-for="(opt, j) in ques.questionAnswerVersionsList" :key="j" class="opt">
<el-checkbox v-if="item.questionType === 'multiple_choice'" v-model="opt.answer" :true-label="1"
@change="mulChange(ques)"></el-checkbox>
<el-radio v-else v-model="opt.answer" :true-label="1" :label="1"
@change="singleChange(ques, j)"></el-radio>
<span>{{ numToLetter(j) }}.&nbsp;</span>
<div class="text" v-html="opt.optionText"></div>
</div>
</template>
<!-- 简答题需要展示题干文件及富文本 -->
<template v-if="item.questionType === 'essay'">
<div v-if="ques.stemAttachment" class="m-b-20">
<el-link class="m-r-10" type="primary">{{ ques.stemAttachment }}</el-link>
<el-button type="primary" size="mini" round @click="download(ques.stemAttachment)">下载</el-button>
</div>
<UeditorPlus :ref="'essayAnswer' + ques.id" v-model="ques.answer"
@ready="editor => essayAnswerReady(editor, ques)" />
<!-- v-if="ques.allowAttachment" -->
<div class="m-t-20">
<div v-if="form.uploadInstructions" class="flex m-b-10 fs-12">
<span>上传附件</span>
<div v-html="form.uploadInstructions"></div>
</div>
<el-upload action="#">
<el-button size="small" type="primary">上传文件</el-button>
</el-upload>
</div>
</template>
</div>
</div>
</li>
</ul>
</div>
</div>
</template>
<script>
import Util from '@/libs/util'
import Setting from "@/setting"
import QuesConst from '@/const/ques'
import TestPaperConst from '@/const/testPaper'
import _ from 'lodash'
import Oss from '@/components/upload/upload.js'
import Upload from '@/components/upload'
import UeditorPlus from '@/components/ueditorPlus'
export default {
components: {
Upload, UeditorPlus
},
data () {
return {
questionTypes: QuesConst.questionTypes,
numToLetter: Util.numToLetter,
arabicToChinese: Util.arabicToChinese,
id: +this.$route.query.id,
entryTime: '',
counterTimer: null, // setInterval
timeSumVal: 0,
//
timeSum: {
day: 0,
seconds: 0,
minutes: 0,
hour: 0,
},
totalAnswered: 0,
progress: 0,
sheetStatus: '',
form: {
questionCount: 0,
},
};
},
mounted () {
this.$once('hook:beforeDestroy', function () {
clearInterval(this.counterTimer)
})
this.getPaper()
},
methods: {
//
async getPaper (now) {
try {
this.entryTime = await Util.getNow()
const { id } = this
if (id) {
//
const { examPaper } = await this.$get(this.api.examPaperDetails, { id })
this.startCount()
const r = examPaper
const paper = r.paperOutline
const types = this.questionTypes
paper.map(e => {
const type = e.questionType
e.questionTypeName = types.find(n => n.id === type).name
e.shrink = false
e.examQuestions.map(n => {
Object.assign(n, n.question)
n.sign = 0
const opts = n.questionAnswerVersionsList
if (type !== 'fill_blank' && type !== 'essay' && opts) { //
opts.map(m => {
m.answer = 0
})
} else { //
n.answer = ''
//
n.uploadList = []
}
})
})
this.form = r
// input
this.$nextTick(() => {
paper.map(e => {
e.examQuestions.map(n => {
if (e.questionType === 'fill_blank') {
const stem = document.querySelector(`#stem` + n.id)
if (stem) {
const inputs = stem.querySelectorAll('.fill-input')
if (inputs) {
for (const e of inputs) {
e.addEventListener('input', () => {
const answers = []
for (const e of inputs) {
e.value && answers.push(e.value)
}
n.answered = answers.length === inputs.length ? 1 : 0
n.partAnswer = answers.length && answers.length !== inputs.length ? 1 : 0
})
}
}
}
}
})
})
this.calcProgress()
})
console.log("🚀 ~ getPaper ~ r:", r)
}
} catch (e) { }
},
//
handleCounter (counterTime, isCount) {
let leave1 = counterTime % (24 * 3600) //
let leave2 = leave1 % 3600 //
let leave3 = leave2 % 60 //
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)
const count = this[isCount ? 'counterVal' : 'timeSum']
count.day = day
count.hour = hour
count.minutes = minutes
count.seconds = seconds
},
//
startCount () {
clearInterval(this.counterTimer)
this.counterTimer = setInterval(() => {
this.timeSumVal >= 0 && this.handleCounter(this.timeSumVal++)
}, 1000)
},
timeFormat (param) {
return param < 10 ? '0' + param : param
},
//
calcProgress () {
let answered = 0
this.form.paperOutline.map(e => {
answered += e.examQuestions.filter(n => n.answered).length
})
this.totalAnswered = answered
this.progress = +(answered / this.form.questionCount * 100).toFixed(1)
},
//
progressFormat (e) {
return this.totalAnswered + '/' + this.form.questionCount
},
//
filterStatus (e) {
this.sheetStatus = this.sheetStatus === e ? '' : e
},
//
getQuesStem (ques) {
let { stem } = ques
if (ques.questionType === 'fill_blank') { //
let { fills } = ques
const regex = /<span class="gapfilling-span" data-id="(.*?)">______<\/span>/g
let match
let index = 0 //
let result = stem
while ((match = regex.exec(stem)) !== null) {
const newInput = `<input type="text" class="fill-input" value="${fills && fills.length ? fills[index] : ''}">`
result = result.replace(match[0], newInput)
index++
}
return result
} else {
return stem
}
},
//
singleChange (ques, j) {
ques.questionAnswerVersionsList.map(e => {
e.answer = 0
})
ques.questionAnswerVersionsList[j].answer = 1
ques.answered = 1
this.calcProgress()
},
//
mulChange (ques) {
ques.answered = ques.questionAnswerVersionsList.some(e => e.answer)
this.calcProgress()
},
//
essayAnswerReady (editor, ques) {
editor.ques = ques
editor.addListener('contentChange', () => {
ques.answered = editor.getContent() ? 1 : 0
this.calcProgress()
})
ques.answer && editor.setContent(ques.answer)
},
//
download (url) {
Util.downloadFile(url, url)
},
handleRemove (ques) {
Oss.del(ques.attachmentUrl)
ques.attachmentName = ''
ques.attachmentUrl = ''
},
uploadSuccess (file, ques) {
ques.attachmentName = file.name
ques.attachmentUrl = file.url
},
//
answerAnalysisReady (editor) {
this.answerAnalysis && editor.setContent(this.answerAnalysis)
},
close () {
window.close()
}
}
};
</script>
<style lang="scss" scoped>
.top {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
color: #fff;
background-color: #5786fc;
.item {
display: inline-flex;
align-items: center;
}
.names {
font-size: 18px;
font-weight: 600;
}
.submit {
width: 106px;
font-size: 15px;
}
.exit {
margin-left: 20px;
cursor: pointer;
&:hover {
opacity: .9;
}
}
}
/deep/.wrap {
display: flex;
padding: 15px;
.left {
width: 300px;
margin-right: 15px;
background-color: #fff;
.title {
padding: 10px 0;
font-size: 16px;
text-align: center;
color: #333;
}
.progress {
padding: 10px;
color: #5a5a5a;
background-color: #d4e9ff;
}
.type-wrap {
max-height: calc(100vh - 248px);
padding: 10px;
overflow: auto;
.type {
margin-bottom: 20px;
}
.stem {
font-size: 15px;
color: #333;
}
}
.serials {
display: flex;
flex-wrap: wrap;
margin-top: 5px;
li {
position: relative;
width: 35px;
margin: 7px 9px;
font-size: 13px;
text-align: center;
line-height: 35px;
color: #505050;
border: 1px solid #d3d3d3;
&.answered {
color: #fff;
background-color: #66b2ff;
border-color: #66b2ff;
}
&.partAnswer {
border-color: #66b2ff;
}
}
.tag {
position: absolute;
top: -2px;
left: -4px;
}
}
.status-filter {
display: flex;
justify-content: space-between;
padding: 10px;
border-top: 1px solid #e5e5e5;
li {
display: inline-flex;
align-items: center;
padding: 0 3px;
font-size: 12px;
color: #333;
cursor: pointer;
border: 1px solid transparent;
&:not(:last-child):before {
content: '';
width: 13px;
height: 13px;
margin-right: 5px;
border: 1px solid #ccc;
}
&:nth-child(2):before {
background-color: #56aaff;
border: 0;
}
&:nth-child(3):before {
border-color: #56aaff;
}
&.active {
font-weight: 600;
color: #007eff;
border-color: #007eff;
&:last-child {
color: #d81e06;
border-color: #d81e06;
}
}
}
}
}
.ques-wrap {
width: calc(100% - 315px);
height: calc(100vh - 80px);
background-color: #fff;
overflow: auto;
&>li {
margin-bottom: 15px;
}
.outline {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 15px;
font-size: 15px;
font-weight: 600;
color: #333;
background-color: #e8f0ff;
}
.shrink {
cursor: pointer;
transition: .5s;
&.active {
transform: rotate(180deg);
}
}
.ques {
max-height: none;
padding: 15px;
margin-bottom: 15px;
transition: all 1.5s;
overflow: hidden;
&.hide {
max-height: 10px;
}
}
.item:not(:last-child) {
padding-bottom: 15px;
margin-bottom: 15px;
border-bottom: 1px dashed #e1e1e1;
}
.stem-wrap {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.tag {
margin-left: 10px;
cursor: pointer;
&:hover {
opacity: .9;
}
}
.label {
padding: 3px 5px;
margin-right: 10px;
font-size: 12px;
line-height: 1;
color: $main-color;
border: 1px solid;
border-radius: 2px;
}
.fill-input {
width: 100px;
height: 28px;
padding: 0 15px;
margin: 0 10px;
font-size: 13px;
line-height: 28px;
color: #606266;
border: 1px solid #DCDEE0;
border-radius: 2px;
&:focus {
outline: none;
}
}
.opt {
display: flex;
flex-wrap: wrap;
align-items: baseline;
padding-left: 10px;
margin-bottom: 5px;
font-size: 14px;
color: #707070;
line-height: 1.6;
.el-radio,
.el-checkbox {
margin-right: 15px;
}
.el-radio__label {
display: none;
}
.text {
max-width: calc(100% - 48px);
}
}
}
}
</style>

@ -20,5 +20,10 @@ export default {
component: () => import('@/pages/testPaper/detail'),
meta: { title: '试卷管理' }
},
{
path: 'preview',
component: () => import('@/pages/testPaper/preview'),
meta: { title: '试卷预览' }
},
]
};

@ -0,0 +1,17 @@
/**
* 试卷
* */
export default {
namespaced: true,
state: {
form: {},
},
mutations: {
setForm: (state, val) => {
state.form = val
},
},
actions: {
}
};

@ -3,6 +3,11 @@
[v-cloak] {
display: none;
}
.ip {
position: fixed;
top: 0;
left: 0;
}
::-webkit-scrollbar {
width: 8px;
height: 8px;

Loading…
Cancel
Save