feat(exam): 增加试卷结果分页查询接口及前端显示功能
- 新增ExamWordsResultReqVO和ExamWordsResultRspVO用于请求与响应封装 - ExamWordsController新增getExamWordsResult方法支持分页查询试卷结果 - ExamWordsJudgeService接口及实现中添加分页获取试卷结果方法及统计总数 - Mapper层添加分页查询和统计的SQL语句支持 - Vue前端uploadpng页面优化为两列布局,新增结果集表格与分页控件 - 上传功能改用自定义http-request,上传后自动刷新结果列表 - Class页面调整布局增加额外展示内容 - 删除未使用接口ExamWordsJudge接口及相关引用 - 重命名ExamWordsJudge相关类和测试类以统一命名规范
This commit is contained in:
@@ -1,11 +1,16 @@
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<router-view></router-view>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 自定义顶部加载 Loading 颜色 */
|
||||
#nprogress .bar {
|
||||
background: #409eff !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
12
enlish-vue/src/api/exam.js
Normal file
12
enlish-vue/src/api/exam.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import axios from "@/axios";
|
||||
|
||||
export function uploadExamWordsPng(data) {
|
||||
return axios.post('/exam/words/genexam', data)
|
||||
}
|
||||
|
||||
export function getExamWordsResult(page, size) {
|
||||
return axios.post('/exam/words/get', {
|
||||
page: page,
|
||||
size: size
|
||||
})
|
||||
}
|
||||
10
enlish-vue/src/axios.js
Normal file
10
enlish-vue/src/axios.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import axios from "axios";
|
||||
|
||||
// 创建 Axios 实例
|
||||
const instance = axios.create({
|
||||
baseURL: "/api", // 你的 API 基础 URL
|
||||
timeout: 7000, // 请求超时时间
|
||||
})
|
||||
|
||||
// 暴露出去
|
||||
export default instance;
|
||||
21
enlish-vue/src/composables/util.js
Normal file
21
enlish-vue/src/composables/util.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'element-plus/es/components/message/style/css'
|
||||
import nprogress from "nprogress"
|
||||
// 消息提示
|
||||
export function showMessage(message = '提示内容', type = 'success', customClass = '') {
|
||||
return ElMessage({
|
||||
type: type,
|
||||
message,
|
||||
customClass,
|
||||
})
|
||||
}
|
||||
|
||||
// 显示页面加载 Loading
|
||||
export function showPageLoading() {
|
||||
nprogress.start()
|
||||
}
|
||||
|
||||
// 隐藏页面加载 Loading
|
||||
export function hidePageLoading() {
|
||||
nprogress.done()
|
||||
}
|
||||
|
||||
80
enlish-vue/src/layouts/components/Header.vue
Normal file
80
enlish-vue/src/layouts/components/Header.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<header>
|
||||
<nav class="bg-white border-gray-200 px-4 lg:px-6 py-2.5 dark:bg-gray-800">
|
||||
<div class="flex flex-wrap justify-between items-center mx-auto max-w-screen-xl">
|
||||
<a href="#" class="flex items-center">
|
||||
<img src="https://flowbite.com/docs/images/logo.svg" class="mr-3 h-6 sm:h-9" alt="Flowbite Logo" />
|
||||
<span class="self-center text-xl font-semibold whitespace-nowrap dark:text-white">Flowbite</span>
|
||||
</a>
|
||||
<div class="flex items-center lg:order-2">
|
||||
<a href="#"
|
||||
class="text-gray-800 dark:text-white hover:bg-gray-50 focus:ring-4 focus:ring-gray-300 font-medium rounded-lg text-sm px-4 lg:px-5 py-2 lg:py-2.5 mr-2 dark:hover:bg-gray-700 focus:outline-none dark:focus:ring-gray-800">Log
|
||||
in</a>
|
||||
<a href="#"
|
||||
class="text-white bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 font-medium rounded-lg text-sm px-4 lg:px-5 py-2 lg:py-2.5 mr-2 dark:bg-primary-600 dark:hover:bg-primary-700 focus:outline-none dark:focus:ring-primary-800">Get
|
||||
started</a>
|
||||
<button data-collapse-toggle="mobile-menu-2" type="button"
|
||||
class="inline-flex items-center p-2 ml-1 text-sm text-gray-500 rounded-lg lg:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600"
|
||||
aria-controls="mobile-menu-2" aria-expanded="false">
|
||||
<span class="sr-only">Open main menu</span>
|
||||
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd"
|
||||
d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
<svg class="hidden w-6 h-6" fill="currentColor" viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd"
|
||||
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="hidden justify-between items-center w-full lg:flex lg:w-auto lg:order-1" id="mobile-menu-2">
|
||||
<ul class="flex flex-col mt-4 font-medium lg:flex-row lg:space-x-8 lg:mt-0">
|
||||
<li>
|
||||
<a href="#"
|
||||
class="block py-2 pr-4 pl-3 text-white rounded bg-primary-700 lg:bg-transparent lg:text-primary-700 lg:p-0 dark:text-white"
|
||||
aria-current="page">Home</a>
|
||||
</li>
|
||||
<li>
|
||||
<router-link
|
||||
to="/class"
|
||||
class="block py-2 pr-4 pl-3 text-gray-700 border-b border-gray-100 hover:bg-gray-50 lg:hover:bg-transparent lg:border-0 lg:hover:text-primary-700 lg:p-0 dark:text-gray-400 lg:dark:hover:text-white dark:hover:bg-gray-700 dark:hover:text-white lg:dark:hover:bg-transparent dark:border-gray-700">
|
||||
班级
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#"
|
||||
class="block py-2 pr-4 pl-3 text-gray-700 border-b border-gray-100 hover:bg-gray-50 lg:hover:bg-transparent lg:border-0 lg:hover:text-primary-700 lg:p-0 dark:text-gray-400 lg:dark:hover:text-white dark:hover:bg-gray-700 dark:hover:text-white lg:dark:hover:bg-transparent dark:border-gray-700">
|
||||
Marketplace
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#"
|
||||
class="block py-2 pr-4 pl-3 text-gray-700 border-b border-gray-100 hover:bg-gray-50 lg:hover:bg-transparent lg:border-0 lg:hover:text-primary-700 lg:p-0 dark:text-gray-400 lg:dark:hover:text-white dark:hover:bg-gray-700 dark:hover:text-white lg:dark:hover:bg-transparent dark:border-gray-700">
|
||||
Features
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#"
|
||||
class="block py-2 pr-4 pl-3 text-gray-700 border-b border-gray-100 hover:bg-gray-50 lg:hover:bg-transparent lg:border-0 lg:hover:text-primary-700 lg:p-0 dark:text-gray-400 lg:dark:hover:text-white dark:hover:bg-gray-700 dark:hover:text-white lg:dark:hover:bg-transparent dark:border-gray-700">
|
||||
Team
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<router-link
|
||||
to="/uploadpng"
|
||||
class="block py-2 pr-4 pl-3 text-gray-700 border-b border-gray-100 hover:bg-gray-50 lg:hover:bg-transparent lg:border-0 lg:hover:text-primary-700 lg:p-0 dark:text-gray-400 lg:dark:hover:text-white dark:hover:bg-gray-700 dark:hover:text-white lg:dark:hover:bg-transparent dark:border-gray-700">
|
||||
上传图片
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
</script>
|
||||
@@ -1,8 +1,13 @@
|
||||
import '@/assets/main.css'
|
||||
import { createApp } from 'vue'
|
||||
import App from '@/App.vue'
|
||||
import 'nprogress/nprogress.css'
|
||||
// 导入路由
|
||||
import router from '@/router'
|
||||
// 导入全局路由守卫
|
||||
import '@/permission'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import App from '@/App.vue'
|
||||
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
|
||||
26
enlish-vue/src/pages/class.vue
Normal file
26
enlish-vue/src/pages/class.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div class="common-layout">
|
||||
<el-container>
|
||||
<el-header>
|
||||
<Header></Header>
|
||||
</el-header>
|
||||
|
||||
<el-main>
|
||||
<div class="flex">
|
||||
<div>
|
||||
班级
|
||||
</div>
|
||||
|
||||
<div>
|
||||
hellow
|
||||
</div>
|
||||
</div>
|
||||
</el-main>
|
||||
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Header from '@/layouts/components/Header.vue'
|
||||
</script>
|
||||
@@ -1,4 +1,26 @@
|
||||
|
||||
<template>
|
||||
|
||||
<div class="common-layout">
|
||||
<el-container>
|
||||
<el-header>
|
||||
<Header></Header>
|
||||
</el-header>
|
||||
|
||||
<el-container>
|
||||
<el-aside width="200px">
|
||||
Aside
|
||||
</el-aside>
|
||||
|
||||
<el-main>
|
||||
Main
|
||||
</el-main>
|
||||
</el-container>
|
||||
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Header from '@/layouts/components/Header.vue'
|
||||
|
||||
</script>
|
||||
113
enlish-vue/src/pages/uploadpng.vue
Normal file
113
enlish-vue/src/pages/uploadpng.vue
Normal file
@@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<div class="common-layout">
|
||||
<el-container>
|
||||
<el-header>
|
||||
<Header></Header>
|
||||
</el-header>
|
||||
|
||||
<el-main class="p-4">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
||||
<div class="text-lg font-semibold mb-4">上传图片</div>
|
||||
<el-upload
|
||||
:show-file-list="false"
|
||||
:http-request="doUpload"
|
||||
accept="image/*"
|
||||
>
|
||||
<el-button type="primary">选择图片并上传</el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
||||
<div class="text-lg font-semibold mb-4">结果集</div>
|
||||
<el-table
|
||||
:data="list"
|
||||
border
|
||||
class="w-full"
|
||||
v-loading="loading"
|
||||
>
|
||||
<el-table-column prop="id" label="ID" width="80" />
|
||||
<el-table-column prop="studentId" label="学生ID" width="100" />
|
||||
<el-table-column prop="examWordsId" label="试题ID" width="100" />
|
||||
<el-table-column prop="correctWordCount" label="正确词数" width="110" />
|
||||
<el-table-column prop="wrongWordCount" label="错误词数" width="110" />
|
||||
<el-table-column label="完成状态" width="110">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.isFinished === 1 ? 'success' : 'info'">
|
||||
{{ row.isFinished === 1 ? '已完成' : '未完成' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="startDate" label="开始时间" min-width="160" />
|
||||
<el-table-column prop="errorMsg" label="错误信息" min-width="160" />
|
||||
</el-table>
|
||||
<div class="mt-4 flex justify-end">
|
||||
<el-pagination
|
||||
background
|
||||
layout="prev, pager, next, sizes, total"
|
||||
:total="totalCount"
|
||||
:page-size="pageSize"
|
||||
:current-page="pageNo"
|
||||
@current-change="handlePageChange"
|
||||
@size-change="handleSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-main>
|
||||
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
import Header from '@/layouts/components/Header.vue'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { uploadExamWordsPng, getExamWordsResult } from '@/api/exam'
|
||||
|
||||
const list = ref([])
|
||||
const pageNo = ref(1)
|
||||
const pageSize = ref(10)
|
||||
const totalCount = ref(0)
|
||||
const loading = ref(false)
|
||||
|
||||
async function fetchList() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getExamWordsResult(pageNo.value, pageSize.value)
|
||||
const d = res.data
|
||||
list.value = Array.isArray(d.data) ? d.data : []
|
||||
totalCount.value = d.totalCount || 0
|
||||
pageNo.value = d.pageNo || pageNo.value
|
||||
pageSize.value = d.pageSize || pageSize.value
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handlePageChange(p) {
|
||||
pageNo.value = p
|
||||
fetchList()
|
||||
}
|
||||
function handleSizeChange(s) {
|
||||
pageSize.value = s
|
||||
pageNo.value = 1
|
||||
fetchList()
|
||||
}
|
||||
|
||||
async function doUpload(options) {
|
||||
const file = options.file
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
await uploadExamWordsPng(formData)
|
||||
ElMessage.success('上传成功,已开始生成结果')
|
||||
fetchList()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
23
enlish-vue/src/permission.js
Normal file
23
enlish-vue/src/permission.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import router from '@/router/index'
|
||||
|
||||
import { showMessage } from '@/composables/util'
|
||||
import { showPageLoading, hidePageLoading } from '@/composables/util'
|
||||
|
||||
// 全局路由前置守卫
|
||||
router.beforeEach((to, from, next) => {
|
||||
console.log('==> 全局路由前置守卫')
|
||||
// 展示页面加载 Loading
|
||||
showPageLoading()
|
||||
next()
|
||||
})
|
||||
|
||||
// 全局路由后置守卫
|
||||
router.afterEach((to, from) => {
|
||||
// 动态设置页面 Title
|
||||
let title = (to.meta.title ? to.meta.title : '') + ' - Weblog'
|
||||
document.title = title
|
||||
|
||||
// 隐藏页面加载 Loading
|
||||
hidePageLoading()
|
||||
})
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import Index from '@/pages/index.vue'
|
||||
import Uploadpng from '@/pages/uploadpng.vue'
|
||||
import Class from '@/pages/class.vue'
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
|
||||
// 统一在这里声明所有路由
|
||||
@@ -7,7 +9,21 @@ const routes = [
|
||||
path: '/', // 路由地址
|
||||
component: Index, // 对应组件
|
||||
meta: { // meta 信息
|
||||
title: 'Weblog 首页' // 页面标题
|
||||
title: '首页' // 页面标题
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/uploadpng', // 路由地址
|
||||
component: Uploadpng, // 对应组件
|
||||
meta: { // meta 信息
|
||||
title: '上传图片' // 页面标题
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/class', // 路由地址
|
||||
component: Class, // 对应组件
|
||||
meta: { // meta 信息
|
||||
title: '班级' // 页面标题
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user