master
parent
c10e676553
commit
bb9a1e6cfb
34 changed files with 1870 additions and 762 deletions
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,179 @@ |
||||
<template> |
||||
<div class="quill" :class="classes"> |
||||
<div ref="editor" :style="styles" v-loading="loading"></div> |
||||
|
||||
<el-upload :action="this.api.fileupload" :before-upload="beforeUpload" :on-success="editorUploadSuccess" style="display: none"> |
||||
<el-button class="editorUpload" size="small" type="primary">点击上传</el-button> |
||||
</el-upload> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import Quill from 'quill'; |
||||
import 'quill/dist/quill.core.css'; |
||||
import 'quill/dist/quill.snow.css'; |
||||
import 'quill/dist/quill.bubble.css'; |
||||
import toolbarOptions from './options' |
||||
|
||||
export default { |
||||
name: 'quill', |
||||
props: { |
||||
value: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
readonly: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
toTop: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
border: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
height: { |
||||
type: Number |
||||
}, |
||||
minHeight: { |
||||
type: Number |
||||
} |
||||
}, |
||||
data () { |
||||
return { |
||||
Quill: null, |
||||
currentValue: '', |
||||
options: { |
||||
theme: 'snow', |
||||
bounds: document.body, |
||||
debug: 'warn', |
||||
modules: { |
||||
toolbar: { |
||||
container: toolbarOptions, |
||||
handlers: { |
||||
'image': function (value) { |
||||
if (value) { |
||||
// 调用iview图片上传 |
||||
document.querySelector('.editorUpload').click() |
||||
} else { |
||||
this.Quill.format('image', false); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
placeholder: '', |
||||
readOnly: this.readonly |
||||
}, |
||||
loading: false |
||||
} |
||||
}, |
||||
computed: { |
||||
classes () { |
||||
return [ |
||||
{ |
||||
'quill-no-border': !this.border |
||||
} |
||||
]; |
||||
}, |
||||
styles () { |
||||
let style = {}; |
||||
if (this.minHeight) { |
||||
style.minHeight = `${this.minHeight}px`; |
||||
} |
||||
if (this.height) { |
||||
style.height = `${this.height}px`; |
||||
} |
||||
return style; |
||||
} |
||||
}, |
||||
watch: { |
||||
value: { |
||||
handler (val) { |
||||
if (val !== this.currentValue) { |
||||
this.currentValue = val; |
||||
if (this.Quill) { |
||||
this.Quill.pasteHTML(this.value); |
||||
} |
||||
} |
||||
}, |
||||
immediate: true |
||||
} |
||||
}, |
||||
mounted () { |
||||
this.init(); |
||||
}, |
||||
beforeDestroy () { |
||||
// 在组件销毁后销毁实例 |
||||
this.Quill = null; |
||||
}, |
||||
methods: { |
||||
init () { |
||||
const editor = this.$refs.editor; |
||||
// 初始化编辑器 |
||||
this.Quill = new Quill(editor, this.options); |
||||
// 默认值 |
||||
this.Quill.pasteHTML(this.currentValue); |
||||
if(this.toTop){ |
||||
this.$nextTick(() => { |
||||
window.scrollTo(0,0) |
||||
}) |
||||
} |
||||
// 绑定事件 |
||||
this.Quill.on('text-change', (delta, oldDelta, source) => { |
||||
const html = this.$refs.editor.children[0].innerHTML; |
||||
const text = this.Quill.getText(); |
||||
const quill = this.Quill; |
||||
// 更新内部的值 |
||||
this.currentValue = html; |
||||
// 发出事件 v-model |
||||
this.$emit('input', html); |
||||
// 发出事件 |
||||
this.$emit('on-change', { html, text, quill }); |
||||
}); |
||||
// 将一些 quill 自带的事件传递出去 |
||||
this.Quill.on('text-change', (delta, oldDelta, source) => { |
||||
this.$emit('on-text-change', delta, oldDelta, source); |
||||
}); |
||||
this.Quill.on('selection-change', (range, oldRange, source) => { |
||||
this.$emit('on-selection-change', range, oldRange, source); |
||||
}); |
||||
this.Quill.on('editor-change', (eventName, ...args) => { |
||||
this.$emit('on-editor-change', eventName, ...args); |
||||
}); |
||||
}, |
||||
beforeUpload(file){ |
||||
this.loading = true |
||||
}, |
||||
editorUploadSuccess (res) { |
||||
// 获取富文本组件实例 |
||||
let quill = this.Quill |
||||
// 如果上传成功 |
||||
if (res.data.filesResult.fileUrl) { |
||||
// 获取光标所在位置 |
||||
let length = quill.getSelection().index; |
||||
// 插入图片,res为服务器返回的图片链接地址 |
||||
quill.insertEmbed(length, 'image', res.data.filesResult.fileUrl) |
||||
// 调整光标到最后 |
||||
quill.setSelection(length + 1) |
||||
} else { |
||||
this.$message.success('图片插入失败') |
||||
} |
||||
this.loading = false |
||||
}, |
||||
} |
||||
} |
||||
</script> |
||||
<style lang="scss" scoped> |
||||
.quill-no-border{ |
||||
.ql-toolbar.ql-snow{ |
||||
border: none; |
||||
border-bottom: 1px solid #e8eaec; |
||||
} |
||||
.ql-container.ql-snow{ |
||||
border: none; |
||||
} |
||||
} |
||||
</style> |
@ -0,0 +1,16 @@ |
||||
export default [ |
||||
['bold', 'italic', 'underline', 'strike'], |
||||
['blockquote', 'code-block'], |
||||
[{ 'header': 1 }, { 'header': 2 }], |
||||
[{ 'list': 'ordered' }, { 'list': 'bullet' }], |
||||
[{ 'script': 'sub' }, { 'script': 'super' }], |
||||
[{ 'indent': '-1' }, { 'indent': '+1' }], |
||||
[{ 'direction': 'rtl' }], |
||||
[{ 'size': ['small', false, 'large', 'huge'] }], |
||||
[{ 'header': [1, 2, 3, 4, 5, 6, false] }], |
||||
[{ 'color': [] }, { 'background': [] }], |
||||
[{ 'font': [] }], |
||||
[{ 'align': [] }], |
||||
['clean'], |
||||
['link', 'image', 'video'] |
||||
] |
@ -0,0 +1,341 @@ |
||||
<template> |
||||
<div> |
||||
<el-card shadow="hover" class="m-b-10"> |
||||
<div class="title m-b-20"><img src="../../../assets/img/index/assesment.png" alt=""> 我的考试安排</div> |
||||
<div> |
||||
<el-date-picker v-model="date" align="right" unlink-panels type="daterange" start-placeholder="开始日期" end-placeholder="结束日期" format="yyyy-MM-dd" value-format="yyyy-MM-dd" clearable></el-date-picker> |
||||
</div> |
||||
</el-card> |
||||
|
||||
<el-card shadow="hover" class="m-b-30"> |
||||
<div class="text-center text-grey m-b-10">待考试列表</div> |
||||
|
||||
<el-table |
||||
:data="listData" |
||||
ref="table" |
||||
row-key="id" |
||||
class="table" |
||||
stripe |
||||
header-align="center" |
||||
> |
||||
<el-table-column type="index" width="100" label="序号" align="center"> |
||||
<template |
||||
slot-scope="scope" |
||||
>{{scope.$index + (page - 1) * pageSize + 1}}</template> |
||||
</el-table-column> |
||||
<el-table-column prop="assessmentName" label="考试名称" align="center"></el-table-column> |
||||
<el-table-column prop="startTime" label="考试开始时间" align="center"></el-table-column> |
||||
<el-table-column prop="endTime" label="考试结束时间" align="center"></el-table-column> |
||||
<el-table-column label="操作" width="200"> |
||||
<template slot-scope="scope"> |
||||
<el-button type="text" @click="toExam(scope.row)" v-if="scope.row.assessmentState == 2" :disabled="scope.row.studentState == 2">进入考试</el-button> |
||||
</template> |
||||
</el-table-column> |
||||
</el-table> |
||||
<div class="pagination"> |
||||
<el-pagination |
||||
background |
||||
@current-change="handleCurrentChange" |
||||
:current-page="page" |
||||
:page-size="pageSize" |
||||
layout="total,prev, pager, next" |
||||
:total="total" |
||||
></el-pagination> |
||||
</div> |
||||
</el-card> |
||||
|
||||
<el-card shadow="hover" class="m-b-10"> |
||||
<div class="title"><img src="../../../assets/img/index/practice.png" alt=""> 我的练习</div> |
||||
</el-card> |
||||
|
||||
<div class="flex m-b-30"> |
||||
<el-card shadow="hover" class="flex-1 m-r-20"> |
||||
<div class="text-center text-grey m-b-10">平均分 {{avgScore}}  总时长 {{totalDuration}}h</div> |
||||
|
||||
<el-table |
||||
:data="practiceData" |
||||
ref="table" |
||||
row-key="id" |
||||
class="table" |
||||
stripe |
||||
header-align="center" |
||||
> |
||||
<el-table-column type="index" width="100" label="序号" align="center"> |
||||
<template |
||||
slot-scope="scope" |
||||
>{{scope.$index + (page - 1) * pageSize + 1}}</template> |
||||
</el-table-column> |
||||
<el-table-column prop="practiseName" label="练习试卷名称" align="center"></el-table-column> |
||||
<el-table-column prop="startTime" label="开始时间" align="center"></el-table-column> |
||||
<el-table-column prop="duration" label="练习时长(分钟)" align="center"></el-table-column> |
||||
<el-table-column prop="lastScore" label="得分" align="center"></el-table-column> |
||||
</el-table> |
||||
<div class="pagination"> |
||||
<el-pagination |
||||
background |
||||
@current-change="handlePracticeCurrentChange" |
||||
:current-page="pagePractice" |
||||
:page-size="pageSizePractice" |
||||
layout="total,prev, pager, next" |
||||
:total="totalPractice" |
||||
></el-pagination> |
||||
</div> |
||||
</el-card> |
||||
|
||||
<el-card shadow="hover" class="flex-1"> |
||||
<div class="chart" id="practiceDuration"></div> |
||||
</el-card> |
||||
</div> |
||||
|
||||
<el-card shadow="hover" class="m-b-10"> |
||||
<div class="title"><img src="../../../assets/img/index/achievement.png" alt=""> 我的成绩</div> |
||||
</el-card> |
||||
<el-card shadow="hover" class="m-b-30"> |
||||
<div class="chart" id="achievement"></div> |
||||
</el-card> |
||||
|
||||
<el-card shadow="hover" class="m-b-10"> |
||||
<div class="title"><img src="../../../assets/img/index/msg.png" alt=""> 回复反馈</div> |
||||
</el-card> |
||||
<el-card shadow="hover"> |
||||
<ul class="list"> |
||||
<li v-for="(item,index) in msgList" :key="index"> |
||||
<div class="item"> |
||||
<div class="inner"> |
||||
<img class="avatar" :src="item.userAvatars" alt=""> |
||||
<div class="texts"> |
||||
<div class="title"> |
||||
<span class="username">{{item.userName}}</span> |
||||
<span class="publish">发表于</span> |
||||
<span class="date">{{item.createTime}}</span> |
||||
</div> |
||||
<div class="desc" v-html="item.content"></div> |
||||
</div> |
||||
</div> |
||||
<div class="action"> |
||||
<button v-if="item.userId != userId" class="btn" @click="showReply(item)">回复</button> |
||||
<button v-else class="btn" @click="delMsg(item)">删除</button> |
||||
</div> |
||||
<div class="reply" v-if="item.showReply"> |
||||
<quill :border="true" v-model="item.replyContent" :toTop="false" :height="150" /> |
||||
<div class="m-t-10 text-right"> |
||||
<el-button type="primary" size="mini" @click="submitComment(item)">提交</el-button> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</li> |
||||
</ul> |
||||
</el-card> |
||||
</div> |
||||
</template> |
||||
<script> |
||||
import { mapState,mapGetters,mapActions } from 'vuex' |
||||
import quill from '@/components/quill' |
||||
import echarts from 'echarts' |
||||
import util from '@/libs/util' |
||||
export default { |
||||
data() { |
||||
return { |
||||
date: [util.formatDate('yyyy-MM-dd'),util.formatDate('yyyy-MM-dd')], |
||||
startTime: '', |
||||
endTime: '', |
||||
page: 1, |
||||
pageSize: 10, |
||||
total: 0, |
||||
listData: [], |
||||
avgScore: 0, |
||||
totalDuration: 0, |
||||
pagePractice: 1, |
||||
pageSizePractice: 10, |
||||
totalPractice: 0, |
||||
practiceData: [], |
||||
practiceDateList: [], |
||||
practiceDurationList: [], |
||||
assesmentNameList: [], |
||||
assesmentScoreList: [], |
||||
msgList: [], |
||||
}; |
||||
}, |
||||
computed: { |
||||
...mapState('user', [ |
||||
'userId','clientId' |
||||
]), |
||||
...mapGetters('assessment', [ |
||||
'getTypeName','getStateName' |
||||
]) |
||||
}, |
||||
watch: { |
||||
date: function(val){ |
||||
if(val){ |
||||
this.startTime = val[0] |
||||
this.endTime = val[1] |
||||
}else{ |
||||
this.startTime = '' |
||||
this.endTime = '' |
||||
} |
||||
this.getData() |
||||
} |
||||
}, |
||||
components: { |
||||
quill |
||||
}, |
||||
mounted() { |
||||
this.getData() |
||||
this.getPractice() |
||||
this.getAchievement() |
||||
this.getMsg() |
||||
}, |
||||
methods: { |
||||
...mapActions('exam', [ |
||||
'setInfo' |
||||
]), |
||||
getData() { |
||||
this.$post(`${this.api.waitExam}?userId=${this.userId}&pageNum=${this.page}&pageSize=${this.pageSize}&startTime=${this.startTime}&endTime=${this.endTime}`) |
||||
.then(res => { |
||||
this.listData = this.handleList(res.data.list.list) |
||||
this.total = res.data.list.totalCount |
||||
}).catch(err => {}) |
||||
}, |
||||
handleCurrentChange(val) { |
||||
this.page = val |
||||
this.getData() |
||||
}, |
||||
toExam(row){ |
||||
this.setInfo({ |
||||
testPaperId: row.testPaperId, |
||||
assessmentId: row.assessmentId, |
||||
teacherId: row.teacherId, |
||||
classId: row.classId, |
||||
duration: row.duration |
||||
}) |
||||
this.$router.push('/exam/do') |
||||
}, |
||||
getPractice() { |
||||
this.$post(`${this.api.getMinePractise}?pageNum=${this.pagePractice}&pageSize=${this.pageSizePractice}&userId=${this.userId}&practiseName=`).then(res => { |
||||
let list = res.data.pageUtils.list |
||||
this.practiceData = list |
||||
this.totalPractice = res.data.pageUtils.totalCount |
||||
|
||||
let avgScore = 0 |
||||
list.map(n => avgScore += n.lastScore) |
||||
this.avgScore = (avgScore / list.length).toFixed(2) |
||||
|
||||
let totalDuration = 0 |
||||
list.map(n => totalDuration += n.duration) |
||||
this.totalDuration = (totalDuration / 60).toFixed(2) |
||||
this.practiceDateList = list.map(n => { |
||||
let date = new Date(n.startTime) |
||||
return `${date.getMonth() + 1}.${date.getDate()}` |
||||
}) |
||||
this.practiceDurationList = list.map(n => n.duration) |
||||
this.getPracticeDuration() |
||||
}).catch(res => {}) |
||||
}, |
||||
handlePracticeCurrentChange(val) { |
||||
this.pagePractice = val |
||||
this.getPractice() |
||||
}, |
||||
getPracticeDuration(){ |
||||
console.log(11,this.practiceDateList,this.practiceDurationList) |
||||
let myChart = echarts.init(document.getElementById('practiceDuration')) |
||||
myChart.setOption({ |
||||
title: { text: '总练习时长' }, |
||||
tooltip: {}, |
||||
xAxis: { |
||||
type: 'category', |
||||
boundaryGap: false, |
||||
data: this.practiceDateList |
||||
}, |
||||
yAxis: { |
||||
type: 'value' |
||||
}, |
||||
series: [{ |
||||
data: this.practiceDurationList, |
||||
type: 'line', |
||||
areaStyle: {} |
||||
}] |
||||
}); |
||||
}, |
||||
getAchievement() { |
||||
this.$post(`${this.api.getMyAchievement}?userId=${this.userId}&pageSize=1000&pageNum=1`).then(res => { |
||||
let list = res.data.list.list |
||||
this.assesmentNameList = list.map(n => n.assessmentName) |
||||
this.assesmentScoreList = list.map(n => n.thisScore) |
||||
this.getAchievementChart() |
||||
}).catch(res => {}) |
||||
}, |
||||
getAchievementChart(){ |
||||
let myChart = echarts.init(document.getElementById('achievement')) |
||||
myChart.setOption({ |
||||
xAxis: { |
||||
type: 'category', |
||||
data: this.assesmentNameList |
||||
}, |
||||
yAxis: { |
||||
type: 'value' |
||||
}, |
||||
series: [{ |
||||
data: this.assesmentScoreList, |
||||
type: 'bar' |
||||
}] |
||||
}); |
||||
}, |
||||
getMsg() { |
||||
this.$post(`${this.api.waitReply}?schoolId=${this.clientId}&userId=${this.userId}`) |
||||
.then(res => { |
||||
this.msgList = this.handleList(res.data.list) |
||||
}).catch(err => {}) |
||||
}, |
||||
handleList(list){ |
||||
list.map(n => { |
||||
n.userAvatars = n.userAvatars ? n.userAvatars : 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png' |
||||
n.showReply = false |
||||
n.replyContent = '' |
||||
n.children = [] |
||||
n.showChildren = false |
||||
}) |
||||
return list |
||||
}, |
||||
showReply(row){ |
||||
row.showReply = !row.showReply |
||||
}, |
||||
delMsg(row){ |
||||
this.$post(`${this.api.waitReplyDel}?userId=${this.userId}&bid=${row.bid}`).then(res => { |
||||
this.$message.success('删除成功') |
||||
this.getMsg() |
||||
}).catch(res => {}) |
||||
}, |
||||
submitComment(row){ |
||||
let data = { |
||||
bid: row.bid, |
||||
content: row.replyContent, |
||||
commentUserId: this.userId, |
||||
schoolId: this.clientId, |
||||
} |
||||
this.$post(this.api.saveComment,data).then(res => { |
||||
this.$message.success('提交成功') |
||||
row.replyContent = '' |
||||
this.getMsg() |
||||
}).catch(res => {}) |
||||
}, |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "@/styles/pages/messageBoard.scss"; |
||||
|
||||
.title{ |
||||
display: flex; |
||||
align-items: center; |
||||
font-size: 16px; |
||||
color: #6f6f6f; |
||||
img{ |
||||
width: 20px; |
||||
margin-right: 5px; |
||||
} |
||||
} |
||||
.chart{ |
||||
height: 400px; |
||||
} |
||||
</style> |
@ -0,0 +1,212 @@ |
||||
<template> |
||||
<div> |
||||
<el-card shadow="hover"> |
||||
<ul class="list"> |
||||
<li v-for="(item,index) in listData" :key="index"> |
||||
<div class="item"> |
||||
<div class="inner"> |
||||
<img class="avatar" :src="item.userAvatars" alt=""> |
||||
<div class="texts"> |
||||
<div class="title"> |
||||
<span class="username">{{item.userName}}</span> |
||||
<span class="publish">发表于</span> |
||||
<span class="date">{{item.createTime}}</span> |
||||
</div> |
||||
<div class="desc" v-html="item.content"></div> |
||||
</div> |
||||
<div class="right"> |
||||
<p class="index">[ {{index+1}} # ]</p> |
||||
</div> |
||||
</div> |
||||
<div class="action"> |
||||
<button v-if="item.userId != userId" class="btn" @click="showReply(item)">回复</button> |
||||
<button v-else class="btn" @click="delMsg(item)">删除</button> |
||||
</div> |
||||
<div class="reply" v-if="item.showReply"> |
||||
<quill :border="true" v-model="item.replyContent" :toTop="false" :height="150" /> |
||||
<div class="m-t-10 text-right"> |
||||
<el-button type="primary" size="mini" @click="submitComment(item)">提交</el-button> |
||||
</div> |
||||
</div> |
||||
|
||||
<ul class="list children" v-if="item.showChildren"> |
||||
<li v-for="(reply,i) in item.children" :key="i"> |
||||
<div class="inner"> |
||||
<img class="avatar" :src="reply.userAvatars" alt=""> |
||||
<div class="texts"> |
||||
<div class="title"> |
||||
<span class="username">{{reply.userName}}</span> |
||||
<span class="publish">发表于</span> |
||||
<span class="date">{{reply.commentTime}}</span> |
||||
</div> |
||||
<div class="desc" v-html="reply.content"></div> |
||||
</div> |
||||
</div> |
||||
<div class="action"> |
||||
<button v-if="reply.commentUserId != userId" class="btn" @click="showReply(reply)">回复</button> |
||||
<button v-else class="btn" @click="delReply(reply,i,index)">删除</button> |
||||
</div> |
||||
<div class="reply" v-if="reply.showReply"> |
||||
<quill :border="true" v-model="reply.replyContent" :toTop="false" :height="150" /> |
||||
<div class="m-t-10 text-right"> |
||||
<el-button type="primary" size="mini" @click="submitReply(reply)">提交</el-button> |
||||
</div> |
||||
</div> |
||||
</li> |
||||
</ul> |
||||
<div v-if="item.getCommentReplyNum" class="toggle"><span @click="toggleReply(item)">{{item.showChildren ? '收起所有回复' : `查看所有${item.getCommentReplyNum}条回复`}} <i class="el-icon-arrow-down"></i></span></div> |
||||
</div> |
||||
</li> |
||||
</ul> |
||||
<div class="pagination"> |
||||
<el-pagination |
||||
background |
||||
@current-change="handleCurrentChange" |
||||
:current-page="page" |
||||
:page-size="pageSize" |
||||
layout="total,prev, pager, next" |
||||
:total="total" |
||||
></el-pagination> |
||||
</div> |
||||
<div class="input-wrap"> |
||||
<quill class="m-t-20" :border="true" v-model="content" :toTop="false" :height="150" /> |
||||
<div class="m-t-10 text-right"> |
||||
<el-button type="primary" size="mini" @click="submitMsg">提交</el-button> |
||||
</div> |
||||
</div> |
||||
</el-card> |
||||
</div> |
||||
</template> |
||||
<script> |
||||
import { mapState } from 'vuex' |
||||
import quill from '@/components/quill' |
||||
export default { |
||||
data() { |
||||
return { |
||||
page: 1, |
||||
pageSize: 10, |
||||
total: 0, |
||||
listData: [], |
||||
content: '' |
||||
}; |
||||
}, |
||||
computed: { |
||||
...mapState('user', [ |
||||
'userId','clientId' |
||||
]) |
||||
}, |
||||
components: { |
||||
quill |
||||
}, |
||||
mounted() { |
||||
this.getData() |
||||
}, |
||||
methods: { |
||||
getData() { |
||||
this.$post(`${this.api.queryMessageBoard}?schoolId=${this.clientId}&pageNum=${this.page}&pageSize=${this.pageSize}`) |
||||
.then(res => { |
||||
this.listData = this.handleList(res.data.list.list) |
||||
this.total = res.data.list.totalCount |
||||
}).catch(err => {}) |
||||
}, |
||||
handleList(list){ |
||||
list.map(n => { |
||||
n.userAvatars = n.userAvatars ? n.userAvatars : 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png' |
||||
n.showReply = false |
||||
n.replyContent = '' |
||||
n.children = [] |
||||
n.showChildren = false |
||||
}) |
||||
return list |
||||
}, |
||||
handleCurrentChange(val) { |
||||
this.page = val |
||||
this.getData() |
||||
}, |
||||
toggleReply(row){ |
||||
if(row.showChildren) return row.showChildren = false |
||||
this.$post(`${this.api.queryMessageBoardDetail}?bid=${row.bid}`).then(res => { |
||||
let children = res.data.list |
||||
let replyList = [] |
||||
children.map(n => { |
||||
n.replyList.map(e => { |
||||
e.bid = n.bid |
||||
e.content = e.replyComment |
||||
e.commentTime = e.replyTime |
||||
e.commentUserId = e.userIdByReply |
||||
}) |
||||
replyList = replyList.concat(n.replyList) |
||||
}) |
||||
children = children.concat(replyList) |
||||
row.children = this.handleList(children) |
||||
row.showChildren = true |
||||
}).catch(res => {}) |
||||
}, |
||||
showReply(row){ |
||||
row.showReply = !row.showReply |
||||
}, |
||||
delMsg(row){ |
||||
this.$post(`${this.api.delMessageBoard}?bid=${row.bid}`).then(res => { |
||||
this.$message.success('删除成功') |
||||
this.getData() |
||||
}).catch(res => {}) |
||||
}, |
||||
submitComment(row){ |
||||
let data = { |
||||
bid: row.bid, |
||||
content: row.replyContent, |
||||
commentUserId: this.userId, |
||||
schoolId: this.clientId, |
||||
} |
||||
this.$post(this.api.saveComment,data).then(res => { |
||||
this.$message.success('提交成功') |
||||
row.replyContent = '' |
||||
this.getData() |
||||
}).catch(res => {}) |
||||
}, |
||||
delReply(row,i,index){ |
||||
if(row.identification == 1){ |
||||
this.$post(`${this.api.delComment}?commentId=${row.commentId}`).then(res => { |
||||
this.$message.success('删除成功') |
||||
this.listData[index].children.splice(i,1) |
||||
}).catch(res => {}) |
||||
}else{ |
||||
this.$post(`${this.api.delReply}?replyId=${row.replyId}`).then(res => { |
||||
this.$message.success('删除成功') |
||||
this.listData[index].children.splice(i,1) |
||||
}).catch(res => {}) |
||||
} |
||||
}, |
||||
submitReply(row){ |
||||
let data = { |
||||
bid: row.bid, |
||||
commentId: row.commentId, |
||||
replyComment: row.replyContent, |
||||
replyUserId: row.commentUserId ? row.commentUserId : row.userIdByReply, |
||||
userId: this.userId, |
||||
} |
||||
this.$post(this.api.saveReply,data).then(res => { |
||||
this.$message.success('提交成功') |
||||
row.replyContent = '' |
||||
this.getData() |
||||
}).catch(res => {}) |
||||
}, |
||||
submitMsg(){ |
||||
let data = { |
||||
content: this.content, |
||||
schoolId: this.clientId, |
||||
userId: this.userId |
||||
} |
||||
this.$post(this.api.saveMessageBoard,data).then(res => { |
||||
this.$message.success('提交成功') |
||||
this.content = '' |
||||
this.getData() |
||||
}).catch(res => {}) |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "@/styles/pages/messageBoard.scss"; |
||||
</style> |
@ -0,0 +1,210 @@ |
||||
<template> |
||||
<div class="box"> |
||||
<div class="left"> |
||||
<p class="title">答题卡</p> |
||||
|
||||
<div class="item"> |
||||
<p class="type">单选题</p> |
||||
<p class="total">(共{{singleCount}}题,合计{{singlePoint}}分)</p> |
||||
<div class="nums"> |
||||
<span v-for="n in singleCount" :class="{active: n <= singleAnsweredCount}" :key="n">{{n}}</span> |
||||
</div> |
||||
</div> |
||||
<div class="item"> |
||||
<p class="type">多选题</p> |
||||
<p class="total">(共{{multipleCount}}题,合计{{multipleChoiceScore}}分)</p> |
||||
<div class="nums"> |
||||
<span v-for="n in multipleCount" :class="{active: n <= multipleAnsweredCount}" :key="n">{{n}}</span> |
||||
</div> |
||||
</div> |
||||
<div class="item"> |
||||
<p class="type">判断题</p> |
||||
<p class="total">(共{{judgeCount}}题,合计{{judgeScore}}分)</p> |
||||
<div class="nums"> |
||||
<span v-for="n in judgeCount" :class="{active: n <= judgeAnsweredCount}" :key="n">{{n}}</span> |
||||
</div> |
||||
</div> |
||||
<div class="item"> |
||||
<p class="type">填空题</p> |
||||
<p class="total">(共{{fillBlankCount}}题,合计{{fillBlanksScore}}分)</p> |
||||
<div class="nums"> |
||||
<span v-for="n in fillBlankCount" :class="{active: n <= fillBlankAnsweredCount}" :key="n">{{n}}</span> |
||||
</div> |
||||
</div> |
||||
<div class="item"> |
||||
<p class="type">简答题</p> |
||||
<p class="total">(共{{briefAnswerCount}}题,合计{{briefAnswerScore}}分)</p> |
||||
<div class="nums"> |
||||
<span v-for="n in briefAnswerCount" :class="{active: n <= briefAnswerAnsweredCount}" :key="n">{{n}}</span> |
||||
</div> |
||||
</div> |
||||
<div class="btn"> |
||||
<el-button size="mini" type="primary" @click="save">提交练习</el-button> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="middle"> |
||||
<div class="ques"> |
||||
<template v-for="(subject,k) in subjects"> |
||||
<div class="item" v-if="subject.length" :key="k"> |
||||
<p v-if="subjects[k].length" class="title">{{questionType[k]}}(共{{subjects[k].length}}题,合计{{scoreList[k]}}分)</p> |
||||
<div class="ques-wrap" v-for="(item,index) in subject" :key="index"> |
||||
<div class="name-wrap"> |
||||
<span class="index">{{index+1}}.</span> |
||||
<div class="name" :class="'stem' + k + index" v-html="item.questionStem"></div> |
||||
</div> |
||||
<div class="options"> |
||||
<template v-if="item.name == '单项选择' || item.name == '判断题'"> |
||||
<div class="option"> |
||||
<el-radio-group v-model="item.val" @change.once="updateProgress"> |
||||
<el-radio v-for="(option,i) in item.options" :key="i" :label="i"> |
||||
{{i}}.{{item.options[i]}} |
||||
</el-radio> |
||||
</el-radio-group> |
||||
</div> |
||||
</template> |
||||
<template v-if="item.name == '多项选择'"> |
||||
<el-checkbox-group v-model="item.val" @change="updateProgress"> |
||||
<el-checkbox class="option-check" :label="i" v-for="(option,i) in item.options" :key="i"> |
||||
{{i}}.{{item.options[i]}} |
||||
</el-checkbox> |
||||
</el-checkbox-group> |
||||
</template> |
||||
<template v-if="item.name == '简答题'"> |
||||
<el-input type="textarea" rows="5" v-model="item.val" @input="updateProgress"></el-input> |
||||
</template> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
<script> |
||||
import { mapState,mapGetters,mapActions } from 'vuex' |
||||
import mixins from '@/mixins/setBackground' |
||||
import util from '@/libs/util' |
||||
export default { |
||||
mixins: [ mixins ], |
||||
data() { |
||||
return { |
||||
subjects: [], |
||||
singleCount: 0, |
||||
multipleCount: 0, |
||||
judgeCount: 0, |
||||
fillBlankCount: 0, |
||||
briefAnswerCount: 0, |
||||
|
||||
singleAnsweredCount: 0, |
||||
multipleAnsweredCount: 0, |
||||
judgeAnsweredCount: 0, |
||||
fillBlankAnsweredCount: 0, |
||||
briefAnswerAnsweredCount: 0, |
||||
|
||||
singlePoint: 0, |
||||
multipleChoiceScore: 0, |
||||
judgeScore: 0, |
||||
fillBlanksScore: 0, |
||||
briefAnswerScore: 0, |
||||
scoreList: [], |
||||
questionType: ['单选题','多选题','判断题','填空题','简答题'], |
||||
}; |
||||
}, |
||||
computed: { |
||||
...mapState('user', [ |
||||
'userId','clientId' |
||||
]), |
||||
...mapState('wrongBook', [ |
||||
'qid' |
||||
]), |
||||
}, |
||||
beforeDestroy(){ |
||||
window.updateProgress = null |
||||
}, |
||||
mounted() { |
||||
this.getData() |
||||
}, |
||||
methods: { |
||||
getData() { |
||||
if(this.qid){ |
||||
this.$post(`${this.api.previewPaper}?qid=${this.qid}`) |
||||
.then(res => { |
||||
let data = res.data |
||||
let subjects = [ |
||||
[...data.list1], |
||||
[...data.list2], |
||||
[...data.list4], |
||||
[...data.list3], |
||||
[...data.list5], |
||||
] |
||||
|
||||
this.singleCount = data.list1.length |
||||
this.multipleCount = data.list2.length |
||||
this.fillBlankCount = data.list3.length |
||||
this.judgeCount = data.list4.length |
||||
this.briefAnswerCount = data.list5.length |
||||
|
||||
this.singlePoint = data.list1.length ? data.list1[0].singleChoiceScore * this.singleCount : 0 |
||||
this.multipleChoiceScore = data.list2.length ? data.list2[0].multipleChoiceScore * this.multipleCount : 0 |
||||
this.judgeScore = data.list4.length ? data.list4[0].judgeScore * this.judgeCount : 0 |
||||
this.fillBlanksScore = data.list3.length ? data.list3[0].fillBlanksScore * this.fillBlankCount : 0 |
||||
this.briefAnswerScore = data.list5.length ? data.list5[0].briefAnswerScore * this.briefAnswerCount : 0 |
||||
this.scoreList = [this.singlePoint,this.multipleChoiceScore,this.fillBlanksScore,this.briefAnswerScore,this.briefAnswerScore] |
||||
|
||||
window.updateProgress = (item) => { |
||||
this.updateProgress(item,1) |
||||
} |
||||
subjects.forEach((e,i) => { |
||||
e.forEach((n,j) => { |
||||
if(i == 1){ |
||||
this.$set(n,'val',[]) |
||||
}else if(i == 3){ |
||||
n.questionStem = n.questionStem.replace(/\(\)\(\)\(\)/g,`<input class="input" data-index="${j}" oninput="updateProgress(this)"></input>`) |
||||
}else{ |
||||
this.$set(n,'val','') |
||||
} |
||||
if(!n.options){ |
||||
let options = {} |
||||
for(let i in n){ |
||||
if(i.includes('option') && n[i]){ |
||||
options[i.replace('option','')] = n[i] |
||||
} |
||||
} |
||||
n.options = options |
||||
} |
||||
n.questionStatus = 0 |
||||
}) |
||||
}) |
||||
this.subjects = subjects |
||||
}).catch(err => {}) |
||||
} |
||||
}, |
||||
save(){ |
||||
this.$message.success('提交成功') |
||||
this.$router.back() |
||||
}, |
||||
updateProgress(item,isFillBlank){ |
||||
let subjects = this.subjects |
||||
if(isFillBlank){ |
||||
let index = item.getAttribute('data-index') |
||||
if([...item.parentElement.querySelectorAll('input')].some(n => n.value)){ |
||||
subjects[3][index].hadAnswer = 1 |
||||
}else{ |
||||
subjects[3][index].hadAnswer = 0 |
||||
} |
||||
this.fillBlankAnsweredCount = subjects[3].filter(n => n.hadAnswer).length |
||||
}else{ |
||||
this.singleAnsweredCount = subjects[0].filter(n => n.val).length |
||||
this.multipleAnsweredCount = subjects[1].filter(n => n.val.length).length |
||||
this.judgeAnsweredCount = subjects[2].filter(n => n.val).length |
||||
this.briefAnswerAnsweredCount = subjects[4].filter(n => n.val).length |
||||
} |
||||
} |
||||
}, |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "@/styles/pages/exam.scss"; |
||||
</style> |
@ -0,0 +1,23 @@ |
||||
import BasicLayout from '@/layouts/home'; |
||||
|
||||
const meta = {}; |
||||
|
||||
const pre = 'index-'; |
||||
|
||||
export default { |
||||
path: '/index', |
||||
name: 'index', |
||||
redirect: { |
||||
name: `${pre}list` |
||||
}, |
||||
meta, |
||||
component: BasicLayout, |
||||
children: [ |
||||
{ |
||||
name: `${pre}list`, |
||||
path: `list`, |
||||
component: () => import('@/pages/index/list'), |
||||
meta: { title: '首页' } |
||||
}, |
||||
] |
||||
}; |
@ -0,0 +1,23 @@ |
||||
import BasicLayout from '@/layouts/home'; |
||||
|
||||
const meta = {}; |
||||
|
||||
const pre = 'messageBoard-'; |
||||
|
||||
export default { |
||||
path: '/messageBoard', |
||||
name: 'messageBoard', |
||||
redirect: { |
||||
name: `${pre}list` |
||||
}, |
||||
meta, |
||||
component: BasicLayout, |
||||
children: [ |
||||
{ |
||||
name: `${pre}list`, |
||||
path: `list`, |
||||
component: () => import('@/pages/messageBoard/list'), |
||||
meta: { title: '交流互动' } |
||||
}, |
||||
] |
||||
}; |
@ -0,0 +1,22 @@ |
||||
/** |
||||
* 我的错题本 |
||||
* */ |
||||
export default { |
||||
namespaced: true, |
||||
state: { |
||||
qid: '' |
||||
}, |
||||
getters: { |
||||
|
||||
}, |
||||
mutations: { |
||||
SET_INFO: (state, info) => { |
||||
state.qid = info.qid |
||||
}, |
||||
}, |
||||
actions: { |
||||
setInfo({ commit },info) { |
||||
commit('SET_INFO',info) |
||||
}, |
||||
} |
||||
} |
@ -0,0 +1,147 @@ |
||||
$borderColor: #ececec; |
||||
.box{ |
||||
display: flex; |
||||
justify-content: space-between; |
||||
width: 90%; |
||||
margin: 0 auto; |
||||
.left,.middle,.right{ |
||||
border: 1px solid $borderColor; |
||||
} |
||||
.left,.right{ |
||||
width: 160px; |
||||
padding: 10px; |
||||
margin-right: 10px; |
||||
box-sizing: border-box; |
||||
.title{ |
||||
padding: 10px 0; |
||||
font-size: 14px; |
||||
color: #444; |
||||
text-align: center; |
||||
} |
||||
.item{ |
||||
padding: 10px 0; |
||||
margin-bottom: 10px; |
||||
border-bottom: 1px solid $borderColor; |
||||
.type,.total{ |
||||
color: #444; |
||||
font-size: 12px; |
||||
} |
||||
.total{ |
||||
margin: 10px 0; |
||||
} |
||||
.nums{ |
||||
display: flex; |
||||
flex-wrap: wrap; |
||||
span{ |
||||
width: 24px; |
||||
margin: 2px 1px; |
||||
line-height: 24px; |
||||
text-align: center; |
||||
color: #888; |
||||
font-size: 10px; |
||||
box-sizing: border-box; |
||||
border: 1px solid #e6e6e6; |
||||
border-radius: 50%; |
||||
} |
||||
.active{ |
||||
color: #fff; |
||||
background-color: #e80909; |
||||
border-color: #e80909; |
||||
} |
||||
} |
||||
} |
||||
.btn{ |
||||
margin: 20px 0; |
||||
text-align: center; |
||||
} |
||||
} |
||||
.middle{ |
||||
flex: 1; |
||||
margin-right: 10px; |
||||
overflow: auto; |
||||
.title{ |
||||
padding: 10px; |
||||
margin-bottom: 10px; |
||||
font-size: 14px; |
||||
color: #444; |
||||
border-bottom: 1px solid $borderColor; |
||||
} |
||||
.ques{ |
||||
.ques-wrap{ |
||||
padding: 0 15px; |
||||
margin: 10px 0 20px; |
||||
} |
||||
.item{ |
||||
margin: 10px 0 20px; |
||||
&:first-child{ |
||||
margin-top: 0; |
||||
} |
||||
.name-wrap{ |
||||
display: flex; |
||||
align-items: center; |
||||
margin-bottom: 10px; |
||||
font-size: 13px; |
||||
color: #444; |
||||
.index{ |
||||
font-size: 13px; |
||||
color: #444; |
||||
} |
||||
/deep/.input{ |
||||
width: 100px; |
||||
height: 28px; |
||||
padding: 0 5px; |
||||
margin: 0 5px; |
||||
color: #444; |
||||
background-color: #fff; |
||||
border: 1px solid #ebebeb; |
||||
box-sizing: border-box; |
||||
&:focus{ |
||||
outline: none; |
||||
} |
||||
&:disabled{ |
||||
background-color: #e8e8e8; |
||||
cursor: not-allowed; |
||||
} |
||||
} |
||||
} |
||||
.options{ |
||||
margin-top: 10px; |
||||
font-size: 14px; |
||||
color: #8b8b8b; |
||||
.option{ |
||||
margin: 5px 0; |
||||
&.selected{ |
||||
font-weight: bold; |
||||
color: #555; |
||||
} |
||||
.el-radio-group{ |
||||
display: flex; |
||||
flex-direction: column; |
||||
.el-radio{ |
||||
margin: 3px 0; |
||||
} |
||||
} |
||||
} |
||||
.option-check{ |
||||
display: block; |
||||
margin-right: 6px; |
||||
} |
||||
/deep/.el-radio__label{ |
||||
padding-left: 6px; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
.right{ |
||||
.time,.ans{ |
||||
font-size: 14px; |
||||
color: #444; |
||||
text-align: center; |
||||
} |
||||
.ans{ |
||||
margin: 20px 0 10px; |
||||
font-size: 15px; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,85 @@ |
||||
.list{ |
||||
li{ |
||||
padding-bottom: 10px; |
||||
border-top: 1px solid #f1f1f1; |
||||
&:first-child{ |
||||
border-top: 0; |
||||
} |
||||
.inner{ |
||||
position: relative; |
||||
display: flex; |
||||
justify-content: space-between; |
||||
padding: 10px 0 20px; |
||||
.avatar{ |
||||
width: 40px; |
||||
height: 40px; |
||||
border-radius: 50%; |
||||
} |
||||
.texts{ |
||||
flex: 1; |
||||
margin-left: 10px; |
||||
.title{ |
||||
margin-bottom: 5px; |
||||
font-size: 14px; |
||||
.username{ |
||||
color: $main-color; |
||||
} |
||||
.publish{ |
||||
margin: 0 5px; |
||||
color: #d6d6d6; |
||||
} |
||||
.date{ |
||||
color: #b5b5b5; |
||||
} |
||||
} |
||||
} |
||||
.right{ |
||||
.index{ |
||||
font-size: 12px; |
||||
color: #ccc; |
||||
} |
||||
} |
||||
} |
||||
.action{ |
||||
text-align: right; |
||||
.btn{ |
||||
padding: 2px 4px; |
||||
color: $main-color; |
||||
font-size: 12px; |
||||
background-color: #fff; |
||||
border: 1px solid; |
||||
border-radius: 4px; |
||||
cursor: pointer; |
||||
&:hover{ |
||||
opacity: .8; |
||||
} |
||||
&:first-child{ |
||||
margin-right: 5px; |
||||
} |
||||
} |
||||
} |
||||
.reply{ |
||||
margin-top: 20px; |
||||
} |
||||
} |
||||
&.children{ |
||||
padding: 0 10px 10px; |
||||
margin: 10px 0 0 30px; |
||||
background-color: #f3f4f6; |
||||
li{ |
||||
border-top-color: #fff; |
||||
} |
||||
} |
||||
} |
||||
.toggle{ |
||||
margin: 10px 0; |
||||
text-align: center; |
||||
color: $main-color; |
||||
font-size: 12px; |
||||
span{ |
||||
cursor: pointer; |
||||
} |
||||
} |
||||
/deep/.quill{ |
||||
background-color: #fff; |
||||
} |
Loading…
Reference in new issue