v3.4.0
This commit is contained in:
219
source/vue/exam-student/src/views/dashboard/index.vue
Normal file
219
source/vue/exam-student/src/views/dashboard/index.vue
Normal file
@@ -0,0 +1,219 @@
|
||||
<template>
|
||||
<div style="margin-top: 10px">
|
||||
<el-row>
|
||||
<el-carousel :interval="5000" arrow="always" type="card">
|
||||
<el-carousel-item >
|
||||
<img src="@/assets/carousel/1.png" class="carousel-img">
|
||||
</el-carousel-item>
|
||||
<el-carousel-item >
|
||||
<img src="@/assets/carousel/2.png" class="carousel-img">
|
||||
</el-carousel-item>
|
||||
<el-carousel-item >
|
||||
<img src="@/assets/carousel/3.png" class="carousel-img">
|
||||
</el-carousel-item>
|
||||
<el-carousel-item >
|
||||
<img src="@/assets/carousel/4.png" class="carousel-img">
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</el-row>
|
||||
<el-row class="app-item-contain">
|
||||
<h3 class="index-title-h3" style="border-left: solid 10px #3651d4;">任务中心</h3>
|
||||
<div style="padding-left: 15px">
|
||||
<el-collapse v-loading="taskLoading" accordion v-if="taskList.length!==0">
|
||||
<el-collapse-item :title="taskItem.title" :name="taskItem.id" :key="taskItem.id" v-for="taskItem in taskList">
|
||||
<table class="index-task-table">
|
||||
<tr v-for="paperItem in taskItem.paperItems" :key="paperItem.examPaperId">
|
||||
<td class="index-task-table-paper">
|
||||
{{paperItem.examPaperName}}
|
||||
</td>
|
||||
<td width="70px">
|
||||
<el-tag :type="statusTagFormatter(paperItem.status)" v-if="paperItem.status !== null" size="mini">
|
||||
{{ statusTextFormatter(paperItem.status) }}
|
||||
</el-tag>
|
||||
</td>
|
||||
<td width="80px">
|
||||
<router-link target="_blank" :to="{path:'/do',query:{id:paperItem.examPaperId}}" v-if="paperItem.status === null">
|
||||
<el-button type="text" size="small">开始答题</el-button>
|
||||
</router-link>
|
||||
<router-link target="_blank" :to="{path:'/edit',query:{id:paperItem.examPaperAnswerId}}" v-else-if="paperItem.status === 1">
|
||||
<el-button type="text" size="small">批改试卷</el-button>
|
||||
</router-link>
|
||||
<router-link target="_blank" :to="{path:'/read',query:{id:paperItem.examPaperAnswerId}}" v-else-if="paperItem.status === 2">
|
||||
<el-button type="text" size="small">查看试卷</el-button>
|
||||
</router-link>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-row class="app-item-contain">
|
||||
<h3 class="index-title-h3">固定试卷</h3>
|
||||
<div style="padding-left: 15px">
|
||||
<el-col :span="4" v-for="(item, index) in fixedPaper" :key="index" :offset="index > 0 ? 1 : 0">
|
||||
<el-card :body-style="{ padding: '0px' }" v-loading="loading">
|
||||
<img src="@/assets/exam-paper/show1.png" class="image">
|
||||
<div style="padding: 14px;">
|
||||
<span>{{item.name}}</span>
|
||||
<div class="bottom clearfix">
|
||||
<router-link target="_blank" :to="{path:'/do',query:{id:item.id}}">
|
||||
<el-button type="text" class="button">开始做题</el-button>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-row class="app-item-contain">
|
||||
<h3 class="index-title-h3" style="border-left: solid 10px rgb(220, 208, 65);">时段试卷</h3>
|
||||
<div style="padding-left: 15px">
|
||||
<el-col :span="4" v-for="(item, index) in timeLimitPaper" :key="index" :offset="index > 0 ? 1 : 0">
|
||||
<el-card :body-style="{ padding: '0px' }" v-loading="loading">
|
||||
<img src="@/assets/exam-paper/show2.png" class="image">
|
||||
<div style="padding: 14px;">
|
||||
<span>{{item.name}}</span>
|
||||
<p class="index-limit-paper-time">
|
||||
<span>{{item.startTime}}</span>
|
||||
<br/>
|
||||
<span>{{item.endTime}}</span>
|
||||
</p>
|
||||
<div class="bottom clearfix">
|
||||
<router-link target="_blank" :to="{path:'/do',query:{id:item.id}}">
|
||||
<el-button type="text" class="button">开始做题</el-button>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-row class="app-item-contain">
|
||||
<h3 class="index-title-h3" style="border-left: solid 10px #e454b1;">推送试卷</h3>
|
||||
<div style="padding-left: 15px">
|
||||
<el-col :span="4" v-for="(o, index) in pushPaper" :key="o" :offset="index > 0 ? 1 : 0">
|
||||
<el-card :body-style="{ padding: '0px' }" v-loading="loading">
|
||||
<img src="@/assets/exam-paper/show3.png" class="image">
|
||||
<div style="padding: 14px;">
|
||||
<span>{{item.name}}</span>
|
||||
<p class="index-limit-paper-time">
|
||||
<span>{{item.startTime}}</span>
|
||||
<br/>
|
||||
<span>{{item.endTime}}</span>
|
||||
</p>
|
||||
<div class="bottom clearfix">
|
||||
<router-link target="_blank" :to="{path:'/do',query:{id:item.id}}">
|
||||
<el-button type="text" class="button">开始做题</el-button>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</div>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import indexApi from '@/api/dashboard'
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
fixedPaper: [],
|
||||
timeLimitPaper: [],
|
||||
pushPaper: [],
|
||||
loading: false,
|
||||
taskLoading: false,
|
||||
taskList: []
|
||||
}
|
||||
},
|
||||
created () {
|
||||
let _this = this
|
||||
this.loading = true
|
||||
indexApi.index().then(re => {
|
||||
_this.fixedPaper = re.response.fixedPaper
|
||||
_this.timeLimitPaper = re.response.timeLimitPaper
|
||||
_this.pushPaper = re.response.pushPaper
|
||||
_this.loading = false
|
||||
})
|
||||
|
||||
this.taskLoading = true
|
||||
indexApi.task().then(re => {
|
||||
_this.taskList = re.response
|
||||
_this.taskLoading = false
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
statusTagFormatter (status) {
|
||||
return this.enumFormat(this.statusTag, status)
|
||||
},
|
||||
statusTextFormatter (status) {
|
||||
return this.enumFormat(this.statusEnum, status)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('enumItem', [
|
||||
'enumFormat'
|
||||
]),
|
||||
...mapState('enumItem', {
|
||||
statusEnum: state => state.exam.examPaperAnswer.statusEnum,
|
||||
statusTag: state => state.exam.examPaperAnswer.statusTag
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.index-title-h3 {
|
||||
font-size: 22px;
|
||||
font-weight: 400;
|
||||
color: #1f2f3d;
|
||||
border-left: solid 10px #2ce8b4;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.el-carousel__item h3 {
|
||||
color: #475669;
|
||||
font-size: 18px;
|
||||
opacity: 0.75;
|
||||
line-height: 300px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.el-carousel__item:nth-child(2n) {
|
||||
background-color: #99a9bf;
|
||||
}
|
||||
|
||||
.el-carousel__item:nth-child(2n+1) {
|
||||
background-color: #d3dce6;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
margin-top: 13px;
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 0;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 50%;
|
||||
height: 80%;
|
||||
display: block;
|
||||
margin: 20px auto 10px auto;
|
||||
}
|
||||
|
||||
.clearfix:before,
|
||||
.clearfix:after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.clearfix:after {
|
||||
clear: both
|
||||
}
|
||||
</style>
|
||||
99
source/vue/exam-student/src/views/error-page/401.vue
Normal file
99
source/vue/exam-student/src/views/error-page/401.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div class="errPage-container">
|
||||
<el-button icon="arrow-left" class="pan-back-btn" @click="back">
|
||||
返回
|
||||
</el-button>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<h1 class="text-jumbo text-ginormous">
|
||||
Oops!
|
||||
</h1>
|
||||
gif来源<a href="https://zh.airbnb.com/" target="_blank">airbnb</a> 页面
|
||||
<h2>你没有权限去该页面</h2>
|
||||
<h6>如有不满请联系你领导</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li>或者你可以去:</li>
|
||||
<li class="link-type">
|
||||
<router-link to="/dashboard">
|
||||
回首页
|
||||
</router-link>
|
||||
</li>
|
||||
<li class="link-type">
|
||||
<a href="https://www.taobao.com/">随便看看</a>
|
||||
</li>
|
||||
<li><a href="#" @click.prevent="dialogVisible=true">点我看图</a></li>
|
||||
</ul>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-dialog :visible.sync="dialogVisible" title="随便看">
|
||||
<img :src="ewizardClap" class="pan-img">
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import errGif from '@/assets/401_images/401.gif'
|
||||
|
||||
export default {
|
||||
name: 'Page401',
|
||||
data () {
|
||||
return {
|
||||
errGif: errGif + '?' + +new Date(),
|
||||
ewizardClap: 'https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646',
|
||||
dialogVisible: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
back () {
|
||||
if (this.$route.query.noGoBack) {
|
||||
this.$router.push({ path: '/dashboard' })
|
||||
} else {
|
||||
this.$router.go(-1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.errPage-container {
|
||||
width: 800px;
|
||||
max-width: 100%;
|
||||
margin: 100px auto;
|
||||
.pan-back-btn {
|
||||
background: #008489;
|
||||
color: #fff;
|
||||
border: none!important;
|
||||
}
|
||||
.pan-gif {
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
.pan-img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
.text-jumbo {
|
||||
font-size: 60px;
|
||||
font-weight: 700;
|
||||
color: #484848;
|
||||
}
|
||||
.list-unstyled {
|
||||
font-size: 14px;
|
||||
li {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
a {
|
||||
color: #008489;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
225
source/vue/exam-student/src/views/error-page/404.vue
Normal file
225
source/vue/exam-student/src/views/error-page/404.vue
Normal file
@@ -0,0 +1,225 @@
|
||||
<template>
|
||||
<div class="wscn-http404-container">
|
||||
<div class="wscn-http404">
|
||||
<div class="pic-404">
|
||||
<img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
|
||||
<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
|
||||
<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
|
||||
<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
|
||||
</div>
|
||||
<div class="bullshit">
|
||||
<div class="bullshit__oops">OOPS!</div>
|
||||
<div class="bullshit__headline">{{ message }}</div>
|
||||
<div class="bullshit__info">请检查你的访问地址是否正确, 或点击下面按钮返回主页.</div>
|
||||
<a href="" class="bullshit__return-home">返回主页</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'Page404',
|
||||
computed: {
|
||||
message () {
|
||||
return '页面未找到...'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wscn-http404-container{
|
||||
transform: translate(-50%,-50%);
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
}
|
||||
.wscn-http404 {
|
||||
position: relative;
|
||||
width: 1200px;
|
||||
padding: 0 50px;
|
||||
overflow: hidden;
|
||||
.pic-404 {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 600px;
|
||||
overflow: hidden;
|
||||
&__parent {
|
||||
width: 100%;
|
||||
}
|
||||
&__child {
|
||||
position: absolute;
|
||||
&.left {
|
||||
width: 80px;
|
||||
top: 17px;
|
||||
left: 220px;
|
||||
opacity: 0;
|
||||
animation-name: cloudLeft;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
&.mid {
|
||||
width: 46px;
|
||||
top: 10px;
|
||||
left: 420px;
|
||||
opacity: 0;
|
||||
animation-name: cloudMid;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1.2s;
|
||||
}
|
||||
&.right {
|
||||
width: 62px;
|
||||
top: 100px;
|
||||
left: 500px;
|
||||
opacity: 0;
|
||||
animation-name: cloudRight;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
@keyframes cloudLeft {
|
||||
0% {
|
||||
top: 17px;
|
||||
left: 220px;
|
||||
opacity: 0;
|
||||
}
|
||||
20% {
|
||||
top: 33px;
|
||||
left: 188px;
|
||||
opacity: 1;
|
||||
}
|
||||
80% {
|
||||
top: 81px;
|
||||
left: 92px;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
top: 97px;
|
||||
left: 60px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes cloudMid {
|
||||
0% {
|
||||
top: 10px;
|
||||
left: 420px;
|
||||
opacity: 0;
|
||||
}
|
||||
20% {
|
||||
top: 40px;
|
||||
left: 360px;
|
||||
opacity: 1;
|
||||
}
|
||||
70% {
|
||||
top: 130px;
|
||||
left: 180px;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
top: 160px;
|
||||
left: 120px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes cloudRight {
|
||||
0% {
|
||||
top: 100px;
|
||||
left: 500px;
|
||||
opacity: 0;
|
||||
}
|
||||
20% {
|
||||
top: 120px;
|
||||
left: 460px;
|
||||
opacity: 1;
|
||||
}
|
||||
80% {
|
||||
top: 180px;
|
||||
left: 340px;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
top: 200px;
|
||||
left: 300px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.bullshit {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 300px;
|
||||
padding: 30px 0;
|
||||
overflow: hidden;
|
||||
&__oops {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
line-height: 40px;
|
||||
color: #1482f0;
|
||||
opacity: 0;
|
||||
margin-bottom: 20px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
&__headline {
|
||||
font-size: 20px;
|
||||
line-height: 24px;
|
||||
color: #222;
|
||||
font-weight: bold;
|
||||
opacity: 0;
|
||||
margin-bottom: 10px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.1s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
&__info {
|
||||
font-size: 13px;
|
||||
line-height: 21px;
|
||||
color: grey;
|
||||
opacity: 0;
|
||||
margin-bottom: 30px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.2s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
&__return-home {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 110px;
|
||||
height: 36px;
|
||||
background: #1482f0;
|
||||
border-radius: 100px;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
opacity: 0;
|
||||
font-size: 14px;
|
||||
line-height: 36px;
|
||||
cursor: pointer;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.3s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
@keyframes slideUp {
|
||||
0% {
|
||||
transform: translateY(60px);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,127 @@
|
||||
<template>
|
||||
<div v-loading="qLoading" style="line-height:1.8">
|
||||
<div v-if="qType==1||qType==2||qType==3||qType==4||qType==5">
|
||||
<div v-if="qType==1" >
|
||||
<div class="q-title" v-html="question.title"/>
|
||||
<div class="q-content">
|
||||
<el-radio-group v-model="answer.content">
|
||||
<el-radio v-for="item in question.items" :key="item.prefix" :label="item.prefix" >
|
||||
<span class="question-prefix">{{item.prefix}}.</span>
|
||||
<span v-html="item.content" class="q-item-span-content"></span>
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="qType==2" >
|
||||
<div class="q-title" v-html="question.title"/>
|
||||
<div class="q-content">
|
||||
<el-checkbox-group v-model="answer.contentArray" >
|
||||
<el-checkbox v-for="item in question.items" :label="item.prefix" :key="item.prefix" >
|
||||
<span class="question-prefix">{{item.prefix}}.</span>
|
||||
<span v-html="item.content" class="q-item-span-content"></span>
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="qType==3" >
|
||||
<div class="q-title" v-html="question.title" style="display: inline;margin-right: 10px"/>
|
||||
<span style="padding-right: 10px;">(</span>
|
||||
<el-radio-group v-model="answer.content">
|
||||
<el-radio v-for="item in question.items" :key="item.prefix" :label="item.prefix">
|
||||
<span v-html="item.content" class="q-item-span-content"></span>
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
<span style="padding-left: 10px;">)</span>
|
||||
</div>
|
||||
<div v-else-if="qType==4" >
|
||||
<div class="q-title" v-html="question.title"/>
|
||||
<div v-if="answer.contentArray!==null">
|
||||
<el-form-item :label="item.prefix" :key="item.prefix" v-for="item in question.items" label-width="50px" style="margin-top: 10px;margin-bottom: 10px;">
|
||||
<el-input v-model="answer.contentArray[item.prefix-1]" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="qType==5">
|
||||
<div class="q-title" v-html="question.title"/>
|
||||
<div>
|
||||
<el-input v-model="answer.content" type="textarea" rows="5" ></el-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="question-answer-show-item" style="margin-top: 15px">
|
||||
<span class="question-show-item">结果:</span>
|
||||
<el-tag :type="doRightTagFormatter(answer.doRight)">
|
||||
{{ doRightTextFormatter(answer.doRight) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="question-answer-show-item">
|
||||
<span class="question-show-item">分数:</span>
|
||||
<span>{{question.score}}</span>
|
||||
</div>
|
||||
<div class="question-answer-show-item">
|
||||
<span class="question-show-item">难度:</span>
|
||||
<el-rate disabled v-model="question.difficult" class="question-show-item"></el-rate>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="question-answer-show-item" style="line-height: 1.8">
|
||||
<span class="question-show-item">解析:</span>
|
||||
<span v-html="question.analyze" class="q-item-span-content" />
|
||||
</div>
|
||||
<div class="question-answer-show-item">
|
||||
<span class="question-show-item">正确答案:</span>
|
||||
<span v-if="qType==1||qType==2 ||qType==5" v-html="question.correct" class="q-item-span-content"/>
|
||||
<span v-if="qType==3" v-html="trueFalseFormatter(question)" class="q-item-span-content"/>
|
||||
<span v-if="qType==4">{{question.correctArray}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
export default {
|
||||
name: 'QuestionShow',
|
||||
props: {
|
||||
question: {
|
||||
type: Object,
|
||||
default: function () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
answer: {
|
||||
type: Object,
|
||||
default: function () {
|
||||
return { id: null, content: '', contentArray: [], doRight: false }
|
||||
}
|
||||
},
|
||||
qLoading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
qType: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
trueFalseFormatter (question) {
|
||||
return question.items.filter(d => d.prefix === question.correct)[0].content
|
||||
},
|
||||
doRightTagFormatter (status) {
|
||||
return this.enumFormat(this.doRightTag, status)
|
||||
},
|
||||
doRightTextFormatter (status) {
|
||||
return this.enumFormat(this.doRightEnum, status)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('enumItem', ['enumFormat']),
|
||||
...mapState('enumItem', {
|
||||
doRightEnum: state => state.exam.question.answer.doRightEnum,
|
||||
doRightTag: state => state.exam.question.answer.doRightTag
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div style="line-height:1.8">
|
||||
<div v-if="qType==1" v-loading="qLoading">
|
||||
<div class="q-title" v-html="question.title"/>
|
||||
<div class="q-content">
|
||||
<el-radio-group v-model="answer.content" @change="answer.completed = true" >
|
||||
<el-radio v-for="item in question.items" :key="item.prefix" :label="item.prefix" >
|
||||
<span class="question-prefix">{{item.prefix}}.</span>
|
||||
<span v-html="item.content" class="q-item-span-content"></span>
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="qType==2" v-loading="qLoading">
|
||||
<div class="q-title" v-html="question.title"/>
|
||||
<div class="q-content">
|
||||
<el-checkbox-group v-model="answer.contentArray" @change="answer.completed = true" >
|
||||
<el-checkbox v-for="item in question.items" :label="item.prefix" :key="item.prefix" >
|
||||
<span class="question-prefix">{{item.prefix}}.</span>
|
||||
<span v-html="item.content" class="q-item-span-content"></span>
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="qType==3" v-loading="qLoading">
|
||||
<div class="q-title" v-html="question.title" style="display: inline;margin-right: 10px"/>
|
||||
<span style="padding-right: 10px;">(</span>
|
||||
<el-radio-group v-model="answer.content" @change="answer.completed = true" >
|
||||
<el-radio v-for="item in question.items" :key="item.prefix" :label="item.prefix" >
|
||||
<span v-html="item.content" class="q-item-span-content"></span>
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
<span style="padding-left: 10px;">)</span>
|
||||
</div>
|
||||
<div v-else-if="qType==4" v-loading="qLoading">
|
||||
<div class="q-title" v-html="question.title"/>
|
||||
<div>
|
||||
<el-form-item :label="item.prefix" :key="item.prefix" v-for="item in question.items" label-width="50px" style="margin-top: 10px;margin-bottom: 10px;">
|
||||
<el-input v-model="answer.contentArray[item.prefix-1]" @change="answer.completed = true" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="qType==5" v-loading="qLoading">
|
||||
<div class="q-title" v-html="question.title"/>
|
||||
<div>
|
||||
<el-input v-model="answer.content" type="textarea" rows="5" @change="answer.completed = true"/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'QuestionShow',
|
||||
props: {
|
||||
question: {
|
||||
type: Object,
|
||||
default: function () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
answer: {
|
||||
type: Object,
|
||||
default: function () {
|
||||
return { id: null, content: '', contentArray: [] }
|
||||
}
|
||||
},
|
||||
qLoading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
qType: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
176
source/vue/exam-student/src/views/exam/paper/do.vue
Normal file
176
source/vue/exam-student/src/views/exam/paper/do.vue
Normal file
@@ -0,0 +1,176 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row class="do-exam-title">
|
||||
<el-col :span="24">
|
||||
<span :key="item.itemOrder" v-for="item in answer.answerItems">
|
||||
<el-tag :type="questionCompleted(item.completed)" class="do-exam-title-tag" @click="goAnchor('#question-'+item.itemOrder)">{{item.itemOrder}}</el-tag>
|
||||
</span>
|
||||
<span class="do-exam-time">
|
||||
<label>剩余时间:</label>
|
||||
<label>{{formatSeconds(remainTime)}}</label>
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="do-exam-title-hidden">
|
||||
<el-col :span="24">
|
||||
<span :key="item.itemOrder" v-for="item in answer.answerItems">
|
||||
<el-tag class="do-exam-title-tag" >{{item.itemOrder}}</el-tag>
|
||||
</span>
|
||||
<span class="do-exam-time">
|
||||
<label>剩余时间:</label>
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-container class="app-item-contain">
|
||||
<el-header class="align-center">
|
||||
<h1>{{form.name}}</h1>
|
||||
<div>
|
||||
<span class="question-title-padding">试卷总分:{{form.score}}</span>
|
||||
<span class="question-title-padding">考试时间:{{form.suggestTime}}分钟</span>
|
||||
</div>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<el-form :model="form" ref="form" v-loading="formLoading" label-width="100px">
|
||||
<el-row :key="index" v-for="(titleItem,index) in form.titleItems">
|
||||
<h3>{{titleItem.name}}</h3>
|
||||
<el-card class="exampaper-item-box" v-if="titleItem.questionItems.length!==0">
|
||||
<el-form-item :key="questionItem.itemOrder" :label="questionItem.itemOrder+'.'"
|
||||
v-for="questionItem in titleItem.questionItems"
|
||||
class="exam-question-item" label-width="50px" :id="'question-'+ questionItem.itemOrder">
|
||||
<QuestionEdit :qType="questionItem.questionType" :question="questionItem"
|
||||
:answer="answer.answerItems[questionItem.itemOrder-1]"/>
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
</el-row>
|
||||
<el-row class="do-align-center">
|
||||
<el-button type="primary" @click="submitForm">提交</el-button>
|
||||
<el-button>取消</el-button>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import { formatSeconds } from '@/utils'
|
||||
import QuestionEdit from '../components/QuestionEdit'
|
||||
import examPaperApi from '@/api/examPaper'
|
||||
import examPaperAnswerApi from '@/api/examPaperAnswer'
|
||||
|
||||
export default {
|
||||
components: { QuestionEdit },
|
||||
data () {
|
||||
return {
|
||||
form: {},
|
||||
formLoading: false,
|
||||
answer: {
|
||||
questionId: null,
|
||||
doTime: 0,
|
||||
answerItems: []
|
||||
},
|
||||
timer: null,
|
||||
remainTime: 0
|
||||
}
|
||||
},
|
||||
created () {
|
||||
let id = this.$route.query.id
|
||||
let _this = this
|
||||
if (id && parseInt(id) !== 0) {
|
||||
_this.formLoading = true
|
||||
examPaperApi.select(id).then(re => {
|
||||
_this.form = re.response
|
||||
_this.remainTime = re.response.suggestTime * 60
|
||||
_this.initAnswer()
|
||||
_this.timeReduce()
|
||||
_this.formLoading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
|
||||
},
|
||||
beforeDestroy () {
|
||||
window.clearInterval(this.timer)
|
||||
},
|
||||
methods: {
|
||||
formatSeconds (theTime) {
|
||||
return formatSeconds(theTime)
|
||||
},
|
||||
timeReduce () {
|
||||
let _this = this
|
||||
this.timer = setInterval(function () {
|
||||
if (_this.remainTime <= 0) {
|
||||
_this.submitForm()
|
||||
} else {
|
||||
++_this.answer.doTime
|
||||
--_this.remainTime
|
||||
}
|
||||
}, 1000)
|
||||
},
|
||||
questionCompleted (completed) {
|
||||
return this.enumFormat(this.doCompletedTag, completed)
|
||||
},
|
||||
goAnchor (selector) {
|
||||
this.$el.querySelector(selector).scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' })
|
||||
},
|
||||
initAnswer () {
|
||||
this.answer.id = this.form.id
|
||||
let titleItemArray = this.form.titleItems
|
||||
for (let tIndex in titleItemArray) {
|
||||
let questionArray = titleItemArray[tIndex].questionItems
|
||||
for (let qIndex in questionArray) {
|
||||
let question = questionArray[qIndex]
|
||||
this.answer.answerItems.push({ questionId: question.id, content: null, contentArray: [], completed: false, itemOrder: question.itemOrder })
|
||||
}
|
||||
}
|
||||
},
|
||||
submitForm () {
|
||||
let _this = this
|
||||
window.clearInterval(_this.timer)
|
||||
_this.formLoading = true
|
||||
examPaperAnswerApi.answerSubmit(this.answer).then(re => {
|
||||
if (re.code === 1) {
|
||||
_this.$alert('试卷得分:' + re.response + '分', '考试结果', {
|
||||
confirmButtonText: '返回考试记录',
|
||||
callback: action => {
|
||||
_this.$router.push('/record/index')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
_this.$message.error(re.message)
|
||||
}
|
||||
_this.formLoading = false
|
||||
}).catch(e => {
|
||||
_this.formLoading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('enumItem', ['enumFormat']),
|
||||
...mapState('enumItem', {
|
||||
doCompletedTag: state => state.exam.question.answer.doCompletedTag
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.align-center {
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.exam-question-item {
|
||||
padding: 10px;
|
||||
|
||||
.el-form-item__label {
|
||||
font-size: 15px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.question-title-padding {
|
||||
padding-left: 25px;
|
||||
padding-right: 25px;
|
||||
}
|
||||
</style>
|
||||
154
source/vue/exam-student/src/views/exam/paper/edit.vue
Normal file
154
source/vue/exam-student/src/views/exam/paper/edit.vue
Normal file
@@ -0,0 +1,154 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row class="do-exam-title" style="background-color: #F5F5DC">
|
||||
<el-col :span="24">
|
||||
<span :key="item.itemOrder" v-for="item in answer.answerItems">
|
||||
<el-tag :type="questionDoRightTag(item.doRight)" class="do-exam-title-tag" @click="goAnchor('#question-'+item.itemOrder)">{{item.itemOrder}}</el-tag>
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="do-exam-title-hidden">
|
||||
<el-col :span="24">
|
||||
<span :key="item.itemOrder" v-for="item in answer.answerItems">
|
||||
<el-tag class="do-exam-title-tag" >{{item.itemOrder}}</el-tag>
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-container class="app-item-contain">
|
||||
<el-header class="align-center">
|
||||
<h1>{{form.name}}</h1>
|
||||
<div>
|
||||
<span class="question-title-padding">试卷得分:{{answer.score}}</span>
|
||||
<span class="question-title-padding">试卷耗时:{{formatSeconds(answer.doTime)}}</span>
|
||||
</div>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<el-form :model="form" ref="form" v-loading="formLoading" label-width="100px">
|
||||
<el-row :key="index" v-for="(titleItem,index) in form.titleItems">
|
||||
<h3>{{titleItem.name}}</h3>
|
||||
<el-card class="exampaper-item-box" v-if="titleItem.questionItems.length!==0">
|
||||
<el-form-item :key="questionItem.itemOrder" :label="questionItem.itemOrder+'.'"
|
||||
v-for="questionItem in titleItem.questionItems"
|
||||
class="exam-question-item" label-width="50px" :id="'question-'+ questionItem.itemOrder">
|
||||
<el-row>
|
||||
<QuestionAnswerShow :qType="questionItem.questionType" :question="questionItem" :answer="answer.answerItems[questionItem.itemOrder-1]"/>
|
||||
</el-row>
|
||||
<el-row v-if="answer.answerItems[questionItem.itemOrder-1].doRight === null">
|
||||
<label style="color: #e6a23c">批改:</label>
|
||||
<el-radio-group v-model="answer.answerItems[questionItem.itemOrder-1].score">
|
||||
<el-radio v-for="item in scoreSelect(questionItem.score)" :key="item" :label="item" >
|
||||
{{item}}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
</el-row>
|
||||
<el-row class="do-align-center">
|
||||
<el-button type="primary" @click="submitForm">提交</el-button>
|
||||
<el-button>取消</el-button>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import { formatSeconds } from '@/utils'
|
||||
import QuestionAnswerShow from '../components/QuestionAnswerShow'
|
||||
import examPaperAnswerApi from '@/api/examPaperAnswer'
|
||||
export default {
|
||||
components: { QuestionAnswerShow },
|
||||
data () {
|
||||
return {
|
||||
form: {},
|
||||
formLoading: false,
|
||||
answer: {
|
||||
id: null,
|
||||
score: 0,
|
||||
doTime: 0,
|
||||
answerItems: [],
|
||||
doRight: false
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
let id = this.$route.query.id
|
||||
let _this = this
|
||||
if (id && parseInt(id) !== 0) {
|
||||
_this.formLoading = true
|
||||
examPaperAnswerApi.read(id).then(re => {
|
||||
_this.form = re.response.paper
|
||||
_this.answer = re.response.answer
|
||||
_this.formLoading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
submitForm () {
|
||||
let _this = this
|
||||
_this.formLoading = true
|
||||
examPaperAnswerApi.edit(this.answer).then(re => {
|
||||
if (re.code === 1) {
|
||||
_this.$alert('试卷得分:' + re.response + '分', '考试结果', {
|
||||
confirmButtonText: '返回考试记录',
|
||||
callback: action => {
|
||||
_this.$router.push('/record/index')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
_this.$message.error(re.message)
|
||||
}
|
||||
_this.formLoading = false
|
||||
}).catch(e => {
|
||||
_this.formLoading = false
|
||||
})
|
||||
},
|
||||
scoreSelect (score) {
|
||||
let array = []
|
||||
for (let i = 0; i <= parseInt(score); i++) {
|
||||
array.push(i.toString())
|
||||
}
|
||||
if (score.indexOf('.') !== -1) {
|
||||
array.push(score)
|
||||
}
|
||||
return array
|
||||
},
|
||||
formatSeconds (theTime) {
|
||||
return formatSeconds(theTime)
|
||||
},
|
||||
questionDoRightTag (status) {
|
||||
return this.enumFormat(this.doRightTag, status)
|
||||
},
|
||||
goAnchor (selector) {
|
||||
this.$el.querySelector(selector).scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' })
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('enumItem', ['enumFormat']),
|
||||
...mapState('enumItem', {
|
||||
doRightTag: state => state.exam.question.answer.doRightTag
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.align-center {
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.exam-question-item{
|
||||
padding: 10px;
|
||||
.el-form-item__label{
|
||||
font-size: 15px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.question-title-padding{
|
||||
padding-left: 25px;
|
||||
padding-right: 25px;
|
||||
}
|
||||
</style>
|
||||
111
source/vue/exam-student/src/views/exam/paper/read.vue
Normal file
111
source/vue/exam-student/src/views/exam/paper/read.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row class="do-exam-title" style="background-color: #F5F5DC">
|
||||
<el-col :span="24">
|
||||
<span :key="item.itemOrder" v-for="item in answer.answerItems">
|
||||
<el-tag :type="questionDoRightTag(item.doRight)" class="do-exam-title-tag" @click="goAnchor('#question-'+item.itemOrder)">{{item.itemOrder}}</el-tag>
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="do-exam-title-hidden">
|
||||
<el-col :span="24">
|
||||
<span :key="item.itemOrder" v-for="item in answer.answerItems">
|
||||
<el-tag class="do-exam-title-tag" >{{item.itemOrder}}</el-tag>
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-container class="app-item-contain">
|
||||
<el-header class="align-center">
|
||||
<h1>{{form.name}}</h1>
|
||||
<div>
|
||||
<span class="question-title-padding">试卷得分:{{answer.score}}</span>
|
||||
<span class="question-title-padding">试卷耗时:{{formatSeconds(answer.doTime)}}</span>
|
||||
</div>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<el-form :model="form" ref="form" v-loading="formLoading" label-width="100px">
|
||||
<el-row :key="index" v-for="(titleItem,index) in form.titleItems">
|
||||
<h3>{{titleItem.name}}</h3>
|
||||
<el-card class="exampaper-item-box" v-if="titleItem.questionItems.length!==0">
|
||||
<el-form-item :key="questionItem.itemOrder" :label="questionItem.itemOrder+'.'"
|
||||
v-for="questionItem in titleItem.questionItems"
|
||||
class="exam-question-item" label-width="50px" :id="'question-'+ questionItem.itemOrder">
|
||||
<QuestionAnswerShow :qType="questionItem.questionType" :question="questionItem" :answer="answer.answerItems[questionItem.itemOrder-1]"/>
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import { formatSeconds } from '@/utils'
|
||||
import QuestionAnswerShow from '../components/QuestionAnswerShow'
|
||||
import examPaperAnswerApi from '@/api/examPaperAnswer'
|
||||
export default {
|
||||
components: { QuestionAnswerShow },
|
||||
data () {
|
||||
return {
|
||||
form: {},
|
||||
formLoading: false,
|
||||
answer: {
|
||||
id: null,
|
||||
score: 0,
|
||||
doTime: 0,
|
||||
answerItems: [],
|
||||
doRight: false
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
let id = this.$route.query.id
|
||||
let _this = this
|
||||
if (id && parseInt(id) !== 0) {
|
||||
_this.formLoading = true
|
||||
examPaperAnswerApi.read(id).then(re => {
|
||||
_this.form = re.response.paper
|
||||
_this.answer = re.response.answer
|
||||
_this.formLoading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatSeconds (theTime) {
|
||||
return formatSeconds(theTime)
|
||||
},
|
||||
questionDoRightTag (status) {
|
||||
return this.enumFormat(this.doRightTag, status)
|
||||
},
|
||||
goAnchor (selector) {
|
||||
this.$el.querySelector(selector).scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' })
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('enumItem', ['enumFormat']),
|
||||
...mapState('enumItem', {
|
||||
doRightTag: state => state.exam.question.answer.doRightTag
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.align-center {
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.exam-question-item{
|
||||
padding: 10px;
|
||||
.el-form-item__label{
|
||||
font-size: 15px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.question-title-padding{
|
||||
padding-left: 25px;
|
||||
padding-right: 25px;
|
||||
}
|
||||
</style>
|
||||
415
source/vue/exam-student/src/views/login/index.vue
Normal file
415
source/vue/exam-student/src/views/login/index.vue
Normal file
@@ -0,0 +1,415 @@
|
||||
<template>
|
||||
<div class="lowin lowin-blue">
|
||||
<div class="lowin-brand">
|
||||
<img src="@/assets/logo2.png" alt="logo" style="margin-top: 12px">
|
||||
</div>
|
||||
<div class="lowin-wrapper">
|
||||
<div class="lowin-box lowin-login">
|
||||
<div class="lowin-box-inner">
|
||||
<el-form ref="loginForm" :model="loginForm" :rules="loginRules">
|
||||
<p>学之思考试系统</p>
|
||||
<div class="lowin-group">
|
||||
<label>用户名 </label>
|
||||
<el-input ref="userName" v-model="loginForm.userName" class="lowin-input" placeholder="用户名" name="userName" type="text" tabindex="1" auto-complete="on"/>
|
||||
</div>
|
||||
<div class="lowin-group password-group">
|
||||
<label>密码 <a href="#" class="forgot-link">忘记密码?</a></label>
|
||||
<el-input class="lowin-input" :key="passwordType" ref="password" v-model="loginForm.password" :type="passwordType"
|
||||
placeholder="密码" name="password" tabindex="2" auto-complete="on" @keyup.native="checkCapslock" @blur="capsTooltip = false" @keyup.enter.native="handleLogin"/>
|
||||
</div>
|
||||
|
||||
<el-button :loading="loading" type="text" class="lowin-btn login-btn" @click.native.prevent="handleLogin">登录</el-button>
|
||||
|
||||
<div class="text-foot">
|
||||
还没有账号?
|
||||
<router-link to="/register" class="register-link">
|
||||
注册
|
||||
</router-link>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapMutations } from 'vuex'
|
||||
import loginApi from '@/api/login'
|
||||
|
||||
export default {
|
||||
name: 'Login',
|
||||
data () {
|
||||
const validateUsername = (rule, value, callback) => {
|
||||
if (value.length < 5) {
|
||||
callback(new Error('用户名不能少于5个字符'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const validatePassword = (rule, value, callback) => {
|
||||
if (value.length < 5) {
|
||||
callback(new Error('密码不能少于5个字符'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
loginForm: {
|
||||
userName: '',
|
||||
password: '',
|
||||
remember: false
|
||||
},
|
||||
loginRules: {
|
||||
userName: [{ required: true, trigger: 'blur', validator: validateUsername }],
|
||||
password: [{ required: true, trigger: 'blur', validator: validatePassword }]
|
||||
},
|
||||
passwordType: 'password',
|
||||
capsTooltip: false,
|
||||
loading: false,
|
||||
showDialog: false
|
||||
}
|
||||
},
|
||||
created () {
|
||||
// window.addEventListener('storage', this.afterQRScan)
|
||||
},
|
||||
mounted () {
|
||||
if (this.loginForm.userName === '') {
|
||||
this.$refs.userName.focus()
|
||||
} else if (this.loginForm.password === '') {
|
||||
this.$refs.password.focus()
|
||||
}
|
||||
},
|
||||
destroyed () {
|
||||
// window.removeEventListener('storage', this.afterQRScan)
|
||||
},
|
||||
methods: {
|
||||
checkCapslock ({ shiftKey, key } = {}) {
|
||||
if (key && key.length === 1) {
|
||||
// eslint-disable-next-line no-mixed-operators
|
||||
if (shiftKey && (key >= 'a' && key <= 'z') || !shiftKey && (key >= 'A' && key <= 'Z')) {
|
||||
this.capsTooltip = true
|
||||
} else {
|
||||
this.capsTooltip = false
|
||||
}
|
||||
}
|
||||
if (key === 'CapsLock' && this.capsTooltip === true) {
|
||||
this.capsTooltip = false
|
||||
}
|
||||
},
|
||||
showPwd () {
|
||||
if (this.passwordType === 'password') {
|
||||
this.passwordType = ''
|
||||
} else {
|
||||
this.passwordType = 'password'
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.$refs.password.focus()
|
||||
})
|
||||
},
|
||||
handleLogin () {
|
||||
let _this = this
|
||||
this.$refs.loginForm.validate(valid => {
|
||||
if (valid) {
|
||||
this.loading = true
|
||||
loginApi.login(this.loginForm).then(function (result) {
|
||||
if (result && result.code === 1) {
|
||||
_this.setUserName(_this.loginForm.userName)
|
||||
_this.$router.push({ path: '/' })
|
||||
} else {
|
||||
_this.loading = false
|
||||
_this.$message.error(result.message)
|
||||
}
|
||||
}).catch(function (reason) {
|
||||
_this.loading = false
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
...mapMutations('user', ['setUserName'])
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.lowin-input{
|
||||
.el-input__inner{
|
||||
background-color: transparent !important;
|
||||
border: 0px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.lowin {
|
||||
/* variables */
|
||||
--color-primary: #44a0b3;
|
||||
--color-grey: rgba(68, 160, 179, .06);
|
||||
--color-dark: rgba(68, 160, 179, .5);
|
||||
--color-semidark: rgba(68, 160, 179, .5);
|
||||
|
||||
text-align: center;
|
||||
margin: 60px 0 0 0;
|
||||
font-family: 'Segoe UI';
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.lowin .lowin-wrapper {
|
||||
-webkit-transition: all 1s;
|
||||
-o-transition: all 1s;
|
||||
transition: all 1s;
|
||||
-webkit-perspective: 1000px;
|
||||
perspective: 1000px;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 360px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.lowin.lowin-blue {
|
||||
--color-primary: #0081C6;
|
||||
--color-grey: rgba(0, 129, 198, .05);
|
||||
--color-dark: rgba(0, 129, 198, .7);
|
||||
--color-semidark: rgba(0, 129, 198, .45);
|
||||
}
|
||||
|
||||
.lowin a {
|
||||
color: var(--color-primary);
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dashed var(--color-semidark);
|
||||
margin-top: -3px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.lowin * {
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.lowin .lowin-brand {
|
||||
overflow: hidden;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin: 0 auto -50px auto;
|
||||
border-radius: 50%;
|
||||
-webkit-box-shadow: 0 4px 40px rgba(0, 0, 0, .07);
|
||||
box-shadow: 0 4px 40px rgba(0, 0, 0, .07);
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.lowin .lowin-brand img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.lowin .lowin-box {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.lowin .lowin-box-inner {
|
||||
background-color: #fff;
|
||||
-webkit-box-shadow: 0 7px 25px rgba(0, 0, 0, .08);
|
||||
box-shadow: 0 7px 25px rgba(0, 0, 0, .08);
|
||||
padding: 60px 25px 25px 25px;
|
||||
text-align: left;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.lowin .lowin-box::after {
|
||||
content: ' ';
|
||||
-webkit-box-shadow: 0 0 25px rgba(0, 0, 0, .1);
|
||||
box-shadow: 0 0 25px rgba(0, 0, 0, .1);
|
||||
-webkit-transform: translate(0, -92.6%) scale(.88);
|
||||
-ms-transform: translate(0, -92.6%) scale(.88);
|
||||
transform: translate(0, -92.6%) scale(.88);
|
||||
border-radius: 3px;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.lowin .lowin-box.lowin-flip {
|
||||
-webkit-transform: rotate3d(0, 1, 0, -180deg);
|
||||
transform: rotate3d(0, 1, 0, -180deg);
|
||||
display: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.lowin .lowin-box p {
|
||||
color: var(--color-semidark);
|
||||
font-weight: 700;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.lowin .lowin-box .lowin-group {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.lowin .lowin-box label {
|
||||
margin-bottom: 5px;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
color: var(--color-semidark);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.lowin .lowin-box label a {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.lowin .lowin-box .lowin-input {
|
||||
background-color: var(--color-grey);
|
||||
color: var(--color-dark);
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
padding: 5px 20px;
|
||||
width: 100%;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.lowin .lowin-box .lowin-btn {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
border: none;
|
||||
color: #fff;
|
||||
padding: 15px;
|
||||
border-radius: 3px;
|
||||
background-color: var(--color-primary);
|
||||
-webkit-box-shadow: 0 2px 7px var(--color-semidark);
|
||||
box-shadow: 0 2px 7px var(--color-semidark);
|
||||
font-weight: 700;
|
||||
outline: 0;
|
||||
cursor: pointer;
|
||||
-webkit-transition: all .5s;
|
||||
-o-transition: all .5s;
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.lowin .lowin-box .lowin-btn:active {
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.lowin .lowin-box .lowin-btn:hover {
|
||||
opacity: .9;
|
||||
}
|
||||
|
||||
.lowin .text-foot {
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
font-weight: 700;
|
||||
margin-top: 20px;
|
||||
color: var(--color-semidark);
|
||||
}
|
||||
|
||||
/* animation */
|
||||
.lowin .lowin-box.lowin-animated {
|
||||
-webkit-animation-name: LowinAnimated;
|
||||
animation-name: LowinAnimated;
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards;
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
.lowin .lowin-box.lowin-animatedback {
|
||||
-webkit-animation-name: LowinAnimatedBack;
|
||||
animation-name: LowinAnimatedBack;
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards;
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
.lowin .lowin-box.lowin-animated-flip {
|
||||
-webkit-animation-name: LowinAnimatedFlip;
|
||||
animation-name: LowinAnimatedFlip;
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards;
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
.lowin .lowin-box.lowin-animated-flipback {
|
||||
-webkit-animation-name: LowinAnimatedFlipBack;
|
||||
animation-name: LowinAnimatedFlipBack;
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards;
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
.lowin .lowin-brand.lowin-animated {
|
||||
-webkit-animation-name: LowinBrandAnimated;
|
||||
animation-name: LowinBrandAnimated;
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards;
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
.lowin .lowin-group.password-group {
|
||||
-webkit-transition: all 1s;
|
||||
-o-transition: all 1s;
|
||||
transition: all 1s;
|
||||
}
|
||||
|
||||
.lowin .lowin-group.password-group.lowin-animated {
|
||||
-webkit-animation-name: LowinPasswordAnimated;
|
||||
animation-name: LowinPasswordAnimated;
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards;
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
animation-timing-function: ease-in-out;
|
||||
-webkit-transform-origin: 0 0;
|
||||
-ms-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
|
||||
.lowin .lowin-group.password-group.lowin-animated-back {
|
||||
-webkit-animation-name: LowinPasswordAnimatedBack;
|
||||
animation-name: LowinPasswordAnimatedBack;
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards;
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
animation-timing-function: ease-in-out;
|
||||
-webkit-transform-origin: 0 0;
|
||||
-ms-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 320px) {
|
||||
.lowin .lowin-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
.lowin .lowin-box {
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
90
source/vue/exam-student/src/views/paper/index.vue
Normal file
90
source/vue/exam-student/src/views/paper/index.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div style="margin-top: 10px" class="app-contain">
|
||||
<el-tabs tab-position="left" v-model="tabId" @tab-click="subjectChange" >
|
||||
<el-tab-pane :label="item.name" :key="item.id" :name="item.id" v-for="item in subjectList" style="margin-left: 20px;" >
|
||||
<el-row style="float: right">
|
||||
<el-radio-group v-model="queryParam.paperType" size="mini" @change="paperTypeChange" >
|
||||
<el-radio v-for="item in paperTypeEnum" size="mini" :key="item.key" :label="item.key">{{item.value}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-row>
|
||||
<el-table v-loading="listLoading" :data="tableData" fit highlight-current-row style="width: 100%">
|
||||
<el-table-column prop="id" label="序号" width="90px"/>
|
||||
<el-table-column prop="name" label="名称" />
|
||||
<el-table-column align="right">
|
||||
<template slot-scope="{row}">
|
||||
<router-link target="_blank" :to="{path:'/do',query:{id:row.id}}">
|
||||
<el-button type="text" size="small">开始答题</el-button>
|
||||
</router-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-show="total>0" :total="total" :background="false" :page.sync="queryParam.pageIndex" :limit.sync="queryParam.pageSize"
|
||||
@pagination="search" style="margin-top: 20px"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import Pagination from '@/components/Pagination'
|
||||
import examPaperApi from '@/api/examPaper'
|
||||
import subjectApi from '@/api/subject'
|
||||
|
||||
export default {
|
||||
components: { Pagination },
|
||||
data () {
|
||||
return {
|
||||
queryParam: {
|
||||
paperType: 1,
|
||||
subjectId: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10
|
||||
},
|
||||
tabId: '',
|
||||
listLoading: true,
|
||||
subjectList: [],
|
||||
tableData: [],
|
||||
total: 0
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.initSubject()
|
||||
},
|
||||
methods: {
|
||||
initSubject () {
|
||||
let _this = this
|
||||
subjectApi.list().then(re => {
|
||||
_this.subjectList = re.response
|
||||
let subjectId = _this.subjectList[0].id
|
||||
_this.queryParam.subjectId = subjectId
|
||||
_this.tabId = subjectId.toString()
|
||||
_this.search()
|
||||
})
|
||||
},
|
||||
search () {
|
||||
this.listLoading = true
|
||||
examPaperApi.pageList(this.queryParam).then(data => {
|
||||
const re = data.response
|
||||
this.tableData = re.list
|
||||
this.total = re.total
|
||||
this.queryParam.pageIndex = re.pageNum
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
paperTypeChange (val) {
|
||||
this.search()
|
||||
},
|
||||
subjectChange (tab, event) {
|
||||
this.queryParam.subjectId = Number(this.tabId)
|
||||
this.search()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState('enumItem', {
|
||||
paperTypeEnum: state => state.exam.examPaper.paperTypeEnum
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
99
source/vue/exam-student/src/views/question-error/index.vue
Normal file
99
source/vue/exam-student/src/views/question-error/index.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div style="margin-top: 10px" class="app-contain">
|
||||
<el-row :gutter="50">
|
||||
<el-col :span="14">
|
||||
<el-table v-loading="listLoading" :data="tableData" fit highlight-current-row style="width: 100%" @row-click="itemSelect">
|
||||
<el-table-column prop="shortTitle" label="题干" show-overflow-tooltip />
|
||||
<el-table-column prop="questionType" label="题型" :formatter="questionTypeFormatter" width="70" />
|
||||
<el-table-column prop="subjectName" label="学科" width="50" />
|
||||
<el-table-column prop="createTime" label="做题时间" width="170" />
|
||||
</el-table>
|
||||
<pagination v-show="total>0" :total="total" :background="false" :page.sync="queryParam.pageIndex" :limit.sync="queryParam.pageSize"
|
||||
@pagination="search" style="margin-top: 20px"/>
|
||||
</el-col>
|
||||
<el-col :span="10" >
|
||||
<el-card class="record-answer-info">
|
||||
<el-form>
|
||||
<el-form-item>
|
||||
<QuestionAnswerShow :qType="selectItem.questionType" :qLoading="qAnswerLoading" :question="selectItem.questionItem" :answer="selectItem.answerItem"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import Pagination from '@/components/Pagination'
|
||||
import questionAnswerApi from '@/api/questionAnswer'
|
||||
import QuestionAnswerShow from '../exam/components/QuestionAnswerShow'
|
||||
|
||||
export default {
|
||||
components: { Pagination, QuestionAnswerShow },
|
||||
data () {
|
||||
return {
|
||||
queryParam: {
|
||||
pageIndex: 1,
|
||||
pageSize: 10
|
||||
},
|
||||
listLoading: false,
|
||||
tableData: [],
|
||||
total: 0,
|
||||
qAnswerLoading: false,
|
||||
selectItem: {
|
||||
questionType: 0,
|
||||
questionItem: null,
|
||||
answerItem: null
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.search()
|
||||
},
|
||||
methods: {
|
||||
search () {
|
||||
this.listLoading = true
|
||||
let _this = this
|
||||
questionAnswerApi.pageList(this.queryParam).then(data => {
|
||||
const re = data.response
|
||||
_this.tableData = re.list
|
||||
_this.total = re.total
|
||||
_this.queryParam.pageIndex = re.pageNum
|
||||
_this.listLoading = false
|
||||
if (re.list.length !== 0) {
|
||||
_this.qAnswerShow(re.list[0].id)
|
||||
}
|
||||
})
|
||||
},
|
||||
itemSelect (row, column, event) {
|
||||
this.qAnswerShow(row.id)
|
||||
},
|
||||
qAnswerShow (id) {
|
||||
let _this = this
|
||||
this.qAnswerLoading = true
|
||||
questionAnswerApi.select(id).then(re => {
|
||||
let response = re.response
|
||||
_this.selectItem.questionType = response.questionVM.questionType
|
||||
_this.selectItem.questionItem = response.questionVM
|
||||
_this.selectItem.answerItem = response.questionAnswerVM
|
||||
_this.qAnswerLoading = false
|
||||
})
|
||||
},
|
||||
questionTypeFormatter (row, column, cellValue, index) {
|
||||
return this.enumFormat(this.questionTypeEnum, cellValue)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('enumItem', ['enumFormat']),
|
||||
...mapState('enumItem', {
|
||||
questionTypeEnum: state => state.exam.question.typeEnum
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
125
source/vue/exam-student/src/views/record/index.vue
Normal file
125
source/vue/exam-student/src/views/record/index.vue
Normal file
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<div style="margin-top: 10px" class="app-contain">
|
||||
<el-row :gutter="50">
|
||||
<el-col :span="18">
|
||||
<el-table v-loading="listLoading" :data="tableData" fit highlight-current-row style="width: 100%" @row-click="itemSelect">
|
||||
<el-table-column prop="id" label="序号" width="90px"/>
|
||||
<el-table-column prop="paperName" label="名称" />
|
||||
<el-table-column prop="subjectName" label="学科" width="70" />
|
||||
<el-table-column label="状态" prop="status" width="100px">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag :type="statusTagFormatter(row.status)">
|
||||
{{ statusTextFormatter(row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="做题时间" width="170" />
|
||||
<el-table-column align="right" width="70">
|
||||
<template slot-scope="{row}">
|
||||
<router-link target="_blank" :to="{path:'/edit',query:{id:row.id}}" v-if="row.status === 1 ">
|
||||
<el-button type="text" size="small">批改</el-button>
|
||||
</router-link>
|
||||
<router-link target="_blank" :to="{path:'/read',query:{id:row.id}}" v-if="row.status === 2 ">
|
||||
<el-button type="text" size="small">查看试卷</el-button>
|
||||
</router-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-show="total>0" :total="total" :background="false" :page.sync="queryParam.pageIndex" :limit.sync="queryParam.pageSize"
|
||||
@pagination="search" style="margin-top: 20px"/>
|
||||
</el-col>
|
||||
<el-col :span="6" >
|
||||
<el-card class="record-answer-info">
|
||||
<el-form label-width="50%" >
|
||||
<el-form-item label="系统判分:">
|
||||
<span>{{selectItem.systemScore}}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="最终得分:">
|
||||
<span>{{selectItem.userScore}}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="试卷总分:">
|
||||
<span>{{selectItem.paperScore}}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="正确题数:">
|
||||
<span>{{selectItem.questionCorrect}}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="总题数:">
|
||||
<span>{{selectItem.questionCount}}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="用时:">
|
||||
<span>{{selectItem.doTime}}</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import Pagination from '@/components/Pagination'
|
||||
import examPaperAnswerApi from '@/api/examPaperAnswer'
|
||||
import { scrollTo } from '@/utils/scroll-to'
|
||||
export default {
|
||||
components: { Pagination },
|
||||
data () {
|
||||
return {
|
||||
queryParam: {
|
||||
pageIndex: 1,
|
||||
pageSize: 10
|
||||
},
|
||||
listLoading: false,
|
||||
tableData: [],
|
||||
total: 0,
|
||||
selectItem: {
|
||||
systemScore: '0',
|
||||
userScore: '0',
|
||||
doTime: '0',
|
||||
paperScore: '0',
|
||||
questionCorrect: 0,
|
||||
questionCount: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.search()
|
||||
scrollTo(0, 800)
|
||||
},
|
||||
methods: {
|
||||
search () {
|
||||
this.listLoading = true
|
||||
let _this = this
|
||||
examPaperAnswerApi.pageList(this.queryParam).then(data => {
|
||||
const re = data.response
|
||||
_this.tableData = re.list
|
||||
_this.total = re.total
|
||||
_this.queryParam.pageIndex = re.pageNum
|
||||
_this.listLoading = false
|
||||
})
|
||||
},
|
||||
itemSelect (row, column, event) {
|
||||
this.selectItem = row
|
||||
},
|
||||
statusTagFormatter (status) {
|
||||
return this.enumFormat(this.statusTag, status)
|
||||
},
|
||||
statusTextFormatter (status) {
|
||||
return this.enumFormat(this.statusEnum, status)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('enumItem', [
|
||||
'enumFormat'
|
||||
]),
|
||||
...mapState('enumItem', {
|
||||
statusEnum: state => state.exam.examPaperAnswer.statusEnum,
|
||||
statusTag: state => state.exam.examPaperAnswer.statusTag
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
358
source/vue/exam-student/src/views/register/index.vue
Normal file
358
source/vue/exam-student/src/views/register/index.vue
Normal file
@@ -0,0 +1,358 @@
|
||||
<template>
|
||||
<div class="lowin lowin-blue">
|
||||
<div class="lowin-brand">
|
||||
<img src="@/assets/logo2.png" alt="logo" style="margin-top: 12px">
|
||||
</div>
|
||||
<div class="lowin-wrapper">
|
||||
<div class="lowin-box lowin-register">
|
||||
<div class="lowin-box-inner">
|
||||
<el-form ref="loginForm" :model="loginForm">
|
||||
<p>学之思考试系统</p>
|
||||
<div class="lowin-group">
|
||||
<label>用户名 </label>
|
||||
<el-input ref="userName" v-model="loginForm.userName" class="lowin-input" placeholder="用户名"
|
||||
name="userName" type="text" tabindex="1" auto-complete="on"/>
|
||||
</div>
|
||||
<div class="lowin-group password-group">
|
||||
<label>密码</label>
|
||||
<el-input class="lowin-input" ref="password" v-model="loginForm.password"
|
||||
placeholder="密码" name="password" tabindex="2" auto-complete="on"
|
||||
@keyup.enter.native="handleLogin"/>
|
||||
</div>
|
||||
<div class="lowin-group">
|
||||
<label>年级 </label>
|
||||
<el-select class="lowin-input" v-model="loginForm.userLevel" placeholder="年级">
|
||||
<el-option v-for="item in levelEnum" :key="item.key" :value="item.key" :label="item.value"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<el-button type="text" class="lowin-btn login-btn" @click.native.prevent="handleRegister">注册</el-button>
|
||||
<div class="text-foot">
|
||||
已有账号?
|
||||
<router-link to="/login" class="login-link">
|
||||
登录
|
||||
</router-link>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapMutations, mapState } from 'vuex'
|
||||
import registerApi from '@/api/register'
|
||||
|
||||
export default {
|
||||
name: 'Login',
|
||||
data () {
|
||||
return {
|
||||
loginForm: {
|
||||
userName: '',
|
||||
password: '',
|
||||
userLevel: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleRegister () {
|
||||
let _this = this
|
||||
registerApi.register(this.loginForm).then(function (result) {
|
||||
if (result && result.code === 1) {
|
||||
_this.$router.push({ path: '/login' })
|
||||
} else {
|
||||
_this.$message.error(result.message)
|
||||
}
|
||||
})
|
||||
},
|
||||
...mapMutations('user', ['setUserName'])
|
||||
},
|
||||
computed: {
|
||||
...mapState('enumItem', {
|
||||
levelEnum: state => state.user.levelEnum
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.lowin-input {
|
||||
.el-input__inner {
|
||||
background-color: transparent !important;
|
||||
border: 0px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.lowin {
|
||||
/* variables */
|
||||
--color-primary: #44a0b3;
|
||||
--color-grey: rgba(68, 160, 179, .06);
|
||||
--color-dark: rgba(68, 160, 179, .5);
|
||||
--color-semidark: rgba(68, 160, 179, .5);
|
||||
|
||||
text-align: center;
|
||||
margin: 20px 0 0 0;
|
||||
font-family: 'Segoe UI';
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.lowin .lowin-wrapper {
|
||||
-webkit-transition: all 1s;
|
||||
-o-transition: all 1s;
|
||||
transition: all 1s;
|
||||
-webkit-perspective: 1000px;
|
||||
perspective: 1000px;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 360px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.lowin.lowin-blue {
|
||||
--color-primary: #0081C6;
|
||||
--color-grey: rgba(0, 129, 198, .05);
|
||||
--color-dark: rgba(0, 129, 198, .7);
|
||||
--color-semidark: rgba(0, 129, 198, .45);
|
||||
}
|
||||
|
||||
.lowin a {
|
||||
color: var(--color-primary);
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dashed var(--color-semidark);
|
||||
margin-top: -3px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.lowin * {
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.lowin .lowin-brand {
|
||||
overflow: hidden;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin: 0 auto -50px auto;
|
||||
border-radius: 50%;
|
||||
-webkit-box-shadow: 0 4px 40px rgba(0, 0, 0, .07);
|
||||
box-shadow: 0 4px 40px rgba(0, 0, 0, .07);
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.lowin .lowin-brand img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.lowin .lowin-box {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.lowin .lowin-box-inner {
|
||||
background-color: #fff;
|
||||
-webkit-box-shadow: 0 7px 25px rgba(0, 0, 0, .08);
|
||||
box-shadow: 0 7px 25px rgba(0, 0, 0, .08);
|
||||
padding: 60px 25px 25px 25px;
|
||||
text-align: left;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.lowin .lowin-box::after {
|
||||
content: ' ';
|
||||
-webkit-box-shadow: 0 0 25px rgba(0, 0, 0, .1);
|
||||
box-shadow: 0 0 25px rgba(0, 0, 0, .1);
|
||||
-webkit-transform: translate(0, -92.6%) scale(.88);
|
||||
-ms-transform: translate(0, -92.6%) scale(.88);
|
||||
transform: translate(0, -92.6%) scale(.88);
|
||||
border-radius: 3px;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.lowin .lowin-box.lowin-flip {
|
||||
-webkit-transform: rotate3d(0, 1, 0, -180deg);
|
||||
transform: rotate3d(0, 1, 0, -180deg);
|
||||
display: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.lowin .lowin-box p {
|
||||
color: var(--color-semidark);
|
||||
font-weight: 700;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.lowin .lowin-box .lowin-group {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.lowin .lowin-box label {
|
||||
margin-bottom: 5px;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
color: var(--color-semidark);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.lowin .lowin-box label a {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.lowin .lowin-box .lowin-input {
|
||||
background-color: var(--color-grey);
|
||||
color: var(--color-dark);
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
padding: 5px 20px;
|
||||
width: 100%;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.lowin .lowin-box .lowin-btn {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
border: none;
|
||||
color: #fff;
|
||||
padding: 15px;
|
||||
border-radius: 3px;
|
||||
background-color: var(--color-primary);
|
||||
-webkit-box-shadow: 0 2px 7px var(--color-semidark);
|
||||
box-shadow: 0 2px 7px var(--color-semidark);
|
||||
font-weight: 700;
|
||||
outline: 0;
|
||||
cursor: pointer;
|
||||
-webkit-transition: all .5s;
|
||||
-o-transition: all .5s;
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.lowin .lowin-box .lowin-btn:active {
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.lowin .lowin-box .lowin-btn:hover {
|
||||
opacity: .9;
|
||||
}
|
||||
|
||||
.lowin .text-foot {
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
font-weight: 700;
|
||||
margin-top: 20px;
|
||||
color: var(--color-semidark);
|
||||
}
|
||||
|
||||
/* animation */
|
||||
.lowin .lowin-box.lowin-animated {
|
||||
-webkit-animation-name: LowinAnimated;
|
||||
animation-name: LowinAnimated;
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards;
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
.lowin .lowin-box.lowin-animatedback {
|
||||
-webkit-animation-name: LowinAnimatedBack;
|
||||
animation-name: LowinAnimatedBack;
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards;
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
.lowin .lowin-box.lowin-animated-flip {
|
||||
-webkit-animation-name: LowinAnimatedFlip;
|
||||
animation-name: LowinAnimatedFlip;
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards;
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
.lowin .lowin-box.lowin-animated-flipback {
|
||||
-webkit-animation-name: LowinAnimatedFlipBack;
|
||||
animation-name: LowinAnimatedFlipBack;
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards;
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
.lowin .lowin-brand.lowin-animated {
|
||||
-webkit-animation-name: LowinBrandAnimated;
|
||||
animation-name: LowinBrandAnimated;
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards;
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
.lowin .lowin-group.password-group {
|
||||
-webkit-transition: all 1s;
|
||||
-o-transition: all 1s;
|
||||
transition: all 1s;
|
||||
}
|
||||
|
||||
.lowin .lowin-group.password-group.lowin-animated {
|
||||
-webkit-animation-name: LowinPasswordAnimated;
|
||||
animation-name: LowinPasswordAnimated;
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards;
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
animation-timing-function: ease-in-out;
|
||||
-webkit-transform-origin: 0 0;
|
||||
-ms-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
|
||||
.lowin .lowin-group.password-group.lowin-animated-back {
|
||||
-webkit-animation-name: LowinPasswordAnimatedBack;
|
||||
animation-name: LowinPasswordAnimatedBack;
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards;
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
animation-timing-function: ease-in-out;
|
||||
-webkit-transform-origin: 0 0;
|
||||
-ms-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 320px) {
|
||||
.lowin .lowin-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.lowin .lowin-box {
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
162
source/vue/exam-student/src/views/user-info/index.vue
Normal file
162
source/vue/exam-student/src/views/user-info/index.vue
Normal file
@@ -0,0 +1,162 @@
|
||||
<template>
|
||||
<div style="margin-top: 10px" class="app-contain">
|
||||
<el-row :gutter="50">
|
||||
<el-col :span="7">
|
||||
<el-card>
|
||||
<div slot="header" class="clearfix">
|
||||
<span>个人信息</span>
|
||||
</div>
|
||||
<el-row style="text-align: center">
|
||||
<el-upload action="/api/student/upload/image" accept=".jpg,.png" :show-file-list="false" :on-success="uploadSuccess">
|
||||
<el-avatar class="el-dropdown-avatar" :size="100" :src="form.imagePath === null ? require('@/assets/avatar.png') : form.imagePath"></el-avatar>
|
||||
</el-upload>
|
||||
</el-row>
|
||||
<el-row class="user-info-userName">
|
||||
<label>{{form.userName}}</label>
|
||||
</el-row>
|
||||
<el-divider/>
|
||||
<el-row class="user-info-fullInfo">
|
||||
<label>姓名:{{form.realName}}</label><br/>
|
||||
<label>年级:{{levelFormatter(form.userLevel)}}</label><br/>
|
||||
<label>注册时间:{{form.createTime}}</label><br/>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="17">
|
||||
<el-card shadow="hover">
|
||||
<el-tabs active-name="event" type="card">
|
||||
<el-tab-pane label="用户动态" name="event">
|
||||
<div class="block">
|
||||
<el-timeline>
|
||||
<el-timeline-item :timestamp="item.createTime" placement="top" :key="item.id" v-for="item in event">
|
||||
<el-card>
|
||||
<p>{{item.content}}</p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="个人资料修改" name="update">
|
||||
<el-form :model="form" ref="form" label-width="100px" v-loading="formLoading" :rules="rules">
|
||||
<el-form-item label="真实姓名:" prop="realName" required>
|
||||
<el-input v-model="form.realName"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="年龄:">
|
||||
<el-input v-model="form.age"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别:">
|
||||
<el-select v-model="form.sex" placeholder="性别" clearable>
|
||||
<el-option v-for="item in sexEnum" :key="item.key" :value="item.key"
|
||||
:label="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期:">
|
||||
<el-date-picker v-model="form.birthDay" type="date" placeholder="选择日期"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机:">
|
||||
<el-input v-model="form.phone"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="年级:" prop="userLevel" required>
|
||||
<el-select v-model="form.userLevel" placeholder="年级">
|
||||
<el-option v-for="item in levelEnum" :key="item.key" :value="item.key"
|
||||
:label="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submitForm">更新</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import userApi from '@/api/user'
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
event: [],
|
||||
form: {
|
||||
userName: '',
|
||||
realName: '',
|
||||
age: '',
|
||||
sex: '',
|
||||
birthDay: null,
|
||||
phone: null,
|
||||
userLevel: null,
|
||||
createTime: null,
|
||||
imagePath: null
|
||||
},
|
||||
formLoading: false,
|
||||
rules: {
|
||||
realName: [
|
||||
{ required: true, message: '请输入真实姓名', trigger: 'blur' }
|
||||
],
|
||||
userLevel: [
|
||||
{ required: true, message: '请选择年级', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
let _this = this
|
||||
userApi.getUserEvent().then(re => {
|
||||
_this.event = re.response
|
||||
})
|
||||
userApi.getCurrentUser().then(re => {
|
||||
_this.form = re.response
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
uploadSuccess (re, file) {
|
||||
if (re.code === 1) {
|
||||
window.location.reload()
|
||||
} else {
|
||||
this.$message.error(re.message)
|
||||
}
|
||||
},
|
||||
submitForm () {
|
||||
let _this = this
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (valid) {
|
||||
this.formLoading = true
|
||||
userApi.update(this.form).then(data => {
|
||||
if (data.code === 1) {
|
||||
_this.$message.success(data.message)
|
||||
} else {
|
||||
_this.$message.error(data.message)
|
||||
}
|
||||
_this.formLoading = false
|
||||
}).catch(e => {
|
||||
_this.formLoading = false
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
levelFormatter (level) {
|
||||
return this.enumFormat(this.levelEnum, level)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('enumItem', [
|
||||
'enumFormat'
|
||||
]),
|
||||
...mapState('enumItem', {
|
||||
sexEnum: state => state.user.sexEnum,
|
||||
levelEnum: state => state.user.levelEnum
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
97
source/vue/exam-student/src/views/user-info/message.vue
Normal file
97
source/vue/exam-student/src/views/user-info/message.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div style="margin-top: 10px" class="app-contain">
|
||||
<el-card style="padding-top: 50px;padding-bottom: 50px">
|
||||
<div class="el-table__empty-text" style="text-align: center;width: 100%" v-if="total ===0">
|
||||
<span>暂无消息</span>
|
||||
</div>
|
||||
<el-collapse @change="handleChange" class="student-message-list" v-if="total !==0 " accordion>
|
||||
<el-collapse-item :name="item.id" :key="item.id" v-for="item in tableData">
|
||||
<template slot="title">
|
||||
{{item.title}}
|
||||
<el-tag style=" margin: 0 8px 0 auto;" :type="readTagFormat(item.readed)">{{readTextFormat(item.readed)}}</el-tag>
|
||||
</template>
|
||||
<el-row>
|
||||
<label>发送人:{{item.sendUserName}}</label>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<label>发送时间:{{item.createTime}}</label>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<label>发送内容:{{item.content}}</label>
|
||||
</el-row>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
<pagination v-show="total>0" :total="total" :background="false" :page.sync="queryParam.pageIndex"
|
||||
:limit.sync="queryParam.pageSize"
|
||||
@pagination="search" style="margin-top: 20px;"/>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters, mapMutations } from 'vuex'
|
||||
import Pagination from '@/components/Pagination'
|
||||
import userApi from '@/api/user'
|
||||
|
||||
export default {
|
||||
components: { Pagination },
|
||||
data () {
|
||||
return {
|
||||
queryParam: {
|
||||
pageIndex: 1,
|
||||
pageSize: 10
|
||||
},
|
||||
|
||||
listLoading: true,
|
||||
tableData: [],
|
||||
total: 0
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.search()
|
||||
},
|
||||
methods: {
|
||||
handleChange (val) {
|
||||
if (val === '') {
|
||||
return
|
||||
}
|
||||
let _this = this
|
||||
let selectItem = this.tableData.filter(d => d.id === val)[0]
|
||||
if (!selectItem.readed) {
|
||||
userApi.read(val).then(re => {
|
||||
selectItem.readed = true
|
||||
_this.messageCountSubtract(1)
|
||||
})
|
||||
}
|
||||
},
|
||||
search () {
|
||||
this.listLoading = true
|
||||
userApi.messagePageList(this.queryParam).then(data => {
|
||||
const re = data.response
|
||||
this.tableData = re.list
|
||||
this.total = re.total
|
||||
this.queryParam.pageIndex = re.pageNum
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
readTagFormat (status) {
|
||||
return this.enumFormat(this.readTag, status)
|
||||
},
|
||||
readTextFormat (status) {
|
||||
return this.enumFormat(this.readText, status)
|
||||
},
|
||||
...mapMutations('user', ['messageCountSubtract'])
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('enumItem', ['enumFormat']),
|
||||
...mapState('enumItem', {
|
||||
readTag: state => state.user.message.readTag,
|
||||
readText: state => state.user.message.readText
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user