提交 ef2779ab authored 作者: 王鹏飞's avatar 王鹏飞

feat: 新增考卷批阅和习题批阅

上级 ae4a660f
import httpRequest from '@/utils/axios'
/**
* 获取考卷批阅列表
*/
export function getExamReivewList(params) {
return httpRequest.get('/api/zy/v3-teacher/examination/examination-list', { params })
}
/**
* 获取习题批阅列表
*/
export function getTestReivewList(params) {
return httpRequest.get('/api/zy/v3-teacher/examination/chapter-examination-list', { params })
}
<template>
<div class="table-list">
<div class="table-list-hd">
<!-- 筛选 -->
<div class="table-list-filter" v-if="filters.length">
<el-form :inline="true" :model="params" ref="filterForm" @submit.native.prevent>
<template v-for="item in filters">
<el-form-item :label="item.label" :prop="item.prop" :key="item.prop">
<template v-if="item.slots">
<slot :name="item.slots" v-bind="{ params }"></slot>
</template>
<template v-else>
<!-- input -->
<el-input
v-model="params[item.prop]"
v-bind="item"
clearable
v-if="item.type === 'input'"
@keyup.native.enter="search"
/>
<!-- select -->
<el-select
v-model="params[item.prop]"
clearable
v-bind="item"
v-if="item.type === 'select'"
@change="search"
>
<template v-for="(option, index) in item.options">
<el-option
:label="option[item.labelKey] || option.label"
:value="option[item.valueKey] || option.value"
:key="index"
></el-option>
</template>
</el-select>
</template>
</el-form-item>
</template>
<el-form-item class="filter-buttons">
<el-button type="primary" icon="el-icon-search" @click="search">查询</el-button>
<el-button icon="el-icon-refresh-left" @click="reset">重置</el-button>
<el-button @click="showMoreFilter" v-if="hasMoreFilter">更多筛选</el-button>
</el-form-item>
</el-form>
</div>
<div class="table-list-hd-aside"><slot name="header-aside" /></div>
</div>
<slot></slot>
<!-- 主体 -->
<div class="table-list-bd">
<slot name="body" v-bind="{ data: dataList }">
<el-table
:data="dataList"
v-loading="loading"
v-bind="$attrs"
v-on="$listeners"
style="height: 100%"
ref="table"
>
<template v-for="item in columns">
<el-table-column v-bind="item" :key="item.prop" v-if="visible(item)">
<template v-slot:default="scope" v-if="item.slots || item.computed">
<slot :name="item.slots" v-bind="scope" v-if="item.slots"></slot>
<div v-html="item.computed(scope)" v-if="item.computed"></div>
</template>
</el-table-column>
</template>
</el-table>
</slot>
</div>
<!-- 底部 -->
<div class="table-list-ft">
<div style="padding: 10px 0">
<slot name="footer"></slot>
</div>
<el-pagination
class="table-list-pagination"
layout="total, prev, pager, next, sizes, jumper"
:page-sizes="[10, 20, 30, 50, 100]"
:page-size="page.size"
:total="page.total"
:current-page.sync="page.currentPage"
@size-change="pageSizeChange"
@current-change="fetchList()"
:hide-on-single-page="true"
v-if="hasPagination"
>
</el-pagination>
</div>
<!-- 更多筛选 -->
<el-drawer title="更多筛选" :visible.sync="moreFilterVisible" ref="drawer">
<div class="more-filter-drawer">
<div class="more-filter">
<el-form :model="params" ref="moreFilterForm">
<template v-for="item in moreFilters">
<el-form-item :label="item.label" :prop="item.prop" :key="item.prop">
<template v-if="item.slots">
<slot :name="item.slots" v-bind="{ params }"></slot>
</template>
<template v-else>
<!-- input -->
<el-input v-model="params[item.prop]" v-bind="item" clearable v-if="item.type === 'input'" />
<!-- select -->
<el-select
v-model="params[item.prop]"
clearable
v-bind="item"
v-if="item.type === 'select'"
style="width: 100%"
>
<template v-for="(option, index) in item.options">
<el-option
:label="option[item.labelKey] || option.label"
:value="option[item.valueKey] || option.value"
:key="index"
></el-option>
</template>
</el-select>
</template>
</el-form-item>
</template>
</el-form>
</div>
<div class="more-filter-buttons">
<el-button @click="cancelMoreFilter">取 消</el-button>
<el-button type="primary" @click="primaryMoreFilter">确定</el-button>
</div>
</div>
</el-drawer>
</div>
</template>
<script>
export default {
name: 'AppList',
props: {
// 接口请求
remote: { type: Object, default: () => ({}) },
// 筛选
filters: { type: Array, default: () => [] },
// 更多筛选
moreFilters: { type: Array, default: () => [] },
// 列表项
columns: { type: Array, default: () => [] },
// 列表数据
data: { type: Array, default: () => [] },
// 是否含有翻页
hasPagination: { type: Boolean, default: true },
// 每页多少条数据
limit: { type: Number, default: 20 }
},
data() {
return {
loading: false,
params: this.remote.params || {},
dataList: this.data,
page: { total: 0, size: this.limit, currentPage: 1 },
moreFilterVisible: false
}
},
watch: {
'remote.params': {
immediate: true,
handler(data) {
this.params = data || {}
}
},
data: {
immediate: true,
handler(data) {
this.dataList = data
}
}
},
computed: {
table() {
return this.$refs.table
},
hasMoreFilter() {
return !!this.moreFilters.length
}
},
methods: {
fetchList(isReset) {
/**
* @param function httpRequest api接口
* @param function beforeRequest 接口请求之前
* @param function callback 接口请求成功回调
*/
const { httpRequest, beforeRequest, callback } = this.remote
if (!httpRequest) {
return
}
// 参数设置
let params = this.params
// 翻页参数设置
if (this.hasPagination) {
params.page = this.page.currentPage
params.page_size = this.page.size
}
// 接口请求之前
if (beforeRequest) {
params = beforeRequest(params, isReset)
}
for (const key in params) {
if (params[key] === '' || params[key] === undefined || params[key] === undefined) {
delete params[key]
}
}
this.loading = true
httpRequest(params)
.then(res => {
const data = res.data || {}
const { list = [], total = 0 } = data
this.page.total = total
this.dataList = callback ? callback(data) : list
})
.catch(() => {
this.page.total = 0
this.dataList = []
})
.finally(() => {
this.loading = false
})
},
// 搜索
search() {
this.page.currentPage = 1
this.fetchList()
},
// 重置
reset() {
// 清空筛选条件
this.$refs.filterForm && this.$refs.filterForm.resetFields()
// 清空更多筛选条件
this.hasMoreFilter && this.$refs.moreFilterForm && this.$refs.moreFilterForm.resetFields()
// 初始化页码
this.page.currentPage = 1
// 刷新列表
this.fetchList(true)
},
// 刷新
refetch(isForce) {
isForce ? this.reset() : this.fetchList()
},
// 页数改变
pageSizeChange(value) {
this.page.currentPage = 1
this.page.size = value
this.fetchList()
},
visible(item) {
return Object.prototype.hasOwnProperty.call(item, 'visible') ? item.visible : true
},
// 显示更多筛选
showMoreFilter() {
this.moreFilterVisible = true
},
// 取消更多筛选
cancelMoreFilter() {
this.moreFilterVisible = false
// 清空筛选条件
this.$refs.moreFilterForm && this.$refs.moreFilterForm.resetFields()
},
// 确定更多筛选
primaryMoreFilter() {
this.moreFilterVisible = false
this.search()
}
},
beforeMount() {
this.fetchList()
}
}
</script>
<style lang="scss">
.table-list {
height: 100%;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
.table-list-hd {
display: flex;
margin-bottom: 10px;
}
.table-list-filter {
flex: 1;
}
.table-list-bd {
flex: 1;
}
.table-list-ft {
display: flex;
align-items: center;
justify-content: space-between;
}
.table-list-pagination {
padding: 10px 0;
text-align: right;
}
.el-table-column--selection .cell {
padding: 0 14px !important;
}
.more-filter-drawer {
height: 100%;
display: flex;
flex-direction: column;
padding: 0 20px 20px;
box-sizing: border-box;
}
.more-filter {
flex: 1;
overflow-y: auto;
}
.more-filter-buttons {
display: flex;
.el-button {
flex: 1;
}
}
</style>
<template>
<el-dialog v-bind="$attrs" v-on="$listeners">
<el-tabs v-model="activeName">
<el-tab-pane :label="`全部(${students.length})`" name="0">
<ul>
<li v-for="student in students" :key="student.id">{{ student.name }}</li>
</ul>
</el-tab-pane>
<el-tab-pane :label="`未批阅(${unreviewedStudents.length})`" name="1">
<ul>
<li v-for="student in unreviewedStudents" :key="student.id">{{ student.name }}</li>
</ul>
</el-tab-pane>
<el-tab-pane :label="`已批阅(${reviewedStudents.length})`" name="2">
<ul>
<li v-for="student in reviewedStudents" :key="student.id">{{ student.name }}</li>
</ul>
</el-tab-pane>
</el-tabs>
</el-dialog>
</template>
<script>
export default {
props: { students: { type: Array, default: () => [] } },
data() {
return {
activeName: '0'
}
},
computed: {
// 未批阅的学生
unreviewedStudents() {
return this.students.filter(item => item.status === 1)
},
// 已批阅的学生
reviewedStudents() {
return this.students.filter(item => item.status === 2)
}
}
}
</script>
<style></style>
...@@ -87,6 +87,14 @@ export default { ...@@ -87,6 +87,14 @@ export default {
icon: 'icon-bianzu6-hong', icon: 'icon-bianzu6-hong',
children: [{ title: '课程库', path: '/course/learn' }] children: [{ title: '课程库', path: '/course/learn' }]
}, },
{
title: '模拟考试',
icon: 'icon-bianzuhong',
children: [
{ title: '考卷批阅', path: '/teacher/exam' },
{ title: '习题批阅', path: '/teacher/test' }
]
},
{ {
title: '实训资源', title: '实训资源',
icon: 'icon-kaoshihong', icon: 'icon-kaoshihong',
......
<template>
<app-container>
<app-list v-bind="tableOptions" ref="list">
<template v-slot:table-name="{ row }">
<el-link type="primary">{{ row.paper_title }}</el-link>
</template>
</app-list>
<review-students :visible.sync="visible" :students="currentClickRow.students"></review-students>
</app-container>
</template>
<script>
import { getExamReivewList } from '@/api/teacher.js'
import AppContainer from '@/components/AppContainer'
import AppList from '@/components/AppList'
import ReviewStudents from '@/components/ReviewStudents'
export default {
components: { AppContainer, AppList, ReviewStudents },
data() {
return {
visible: false,
currentClickRow: {}
}
},
computed: {
// 列表配置
tableOptions() {
return {
remote: {
httpRequest: getExamReivewList,
params: { class_name: '', paper_title: '' }
},
filters: [
{ type: 'input', prop: 'class_name', placeholder: '班级名称' },
{ type: 'input', prop: 'paper_title', placeholder: '考卷名称' }
],
columns: [
{ label: '考卷名称', prop: 'paper_title', slots: 'table-name' },
{ label: '班级名称', prop: 'class_name', align: 'center' },
{ label: '未批阅学生数量', prop: 'commit_num', align: 'center' }
]
}
}
},
methods: {
handleRowClick(row) {
this.currentClickRow = row
this.visible = true
}
}
}
</script>
<template>
<app-container>
<app-list v-bind="tableOptions" ref="list" @row-click="handleRowClick">
<template v-slot:table-name="{ row }">
<el-link type="primary">{{ row.paper_title }}</el-link>
</template>
</app-list>
<review-students :visible.sync="visible" :students="currentClickRow.students"></review-students>
</app-container>
</template>
<script>
import { getTestReivewList } from '@/api/teacher.js'
import AppContainer from '@/components/AppContainer'
import AppList from '@/components/AppList'
import ReviewStudents from '@/components/ReviewStudents'
export default {
components: { AppContainer, AppList, ReviewStudents },
data() {
return {
visible: false,
currentClickRow: {}
}
},
computed: {
// 列表配置
tableOptions() {
return {
remote: {
httpRequest: getTestReivewList,
params: { course_id: '', chapter_id: '' }
},
filters: [
{ type: 'select', prop: 'course_id', placeholder: '选择课程' },
{ type: 'select', prop: 'chapter_id', placeholder: '选择章节' }
],
columns: [
{ label: '考卷名称', prop: 'paper_title', slots: 'table-name' },
{ label: '班级名称', prop: 'class_name', align: 'center' },
{ label: '未批阅学生数量', prop: 'commit_num', align: 'center' }
]
}
}
},
methods: {
handleRowClick(row) {
this.currentClickRow = row
this.visible = true
}
}
}
</script>
...@@ -56,9 +56,17 @@ const examAnswer = [ ...@@ -56,9 +56,17 @@ const examAnswer = [
const teacherRoutes = [ const teacherRoutes = [
/* 模拟考试 */ /* 模拟考试 */
{
path: '/teacher/exam',
component: () => import(/* webpackChunkName: "teacher" */ '@/pages/teacher/examReview/index')
},
{
path: '/teacher/test',
component: () => import(/* webpackChunkName: "teacher" */ '@/pages/teacher/testReview/index')
},
{ {
path: '/teacher/practicalCourse', path: '/teacher/practicalCourse',
component: () => import(/* webpackChunkName: "exam" */ '@/pages/teacher/practicalCourse/index') component: () => import(/* webpackChunkName: "teacher" */ '@/pages/teacher/practicalCourse/index')
} }
] ]
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论