- 新增移动端全屏对话框支持及标签宽度和位置动态调整,优化新增班级、年级和学生弹窗布局 - 所有对话框增加屏幕宽度监听,实现自动切换移动端和桌面端样式 - 表格组件增加移动端列表视图,隐藏侧边栏并改进分页和按钮自适应,提升小屏幕浏览体验 - Dialog及详情弹窗添加最大高度限制并启用滚动,防止移动端显示区域拥挤 - 登录页增加安全区域内边距,保证iOS等设备显示完整性 - 新增移动端菜单抽屉组件,支持手机端侧边栏交互显示 - 学生详情页调整词汇热力图列数,实现移动端更合理布局 - 表格和按钮统一增设触控友好大尺寸区域,提升移动端操作便利性 - 修正后端空词汇ID查询问题,避免空列表导致查询异常 - 统一隐藏小屏幕时的固定侧边栏,避免界面混乱和重复显示 - 搜索页和上传页表格添加移动端适配样式和展开收起逻辑,提升列表浏览灵活性
213 lines
9.8 KiB
Vue
213 lines
9.8 KiB
Vue
<template>
|
||
<div class="common-layout">
|
||
<el-container class="min-h-screen">
|
||
<el-header>
|
||
<Header></Header>
|
||
</el-header>
|
||
|
||
<el-container class="pt-4">
|
||
<el-aside width="200px" class="hidden md:block sidebar-fixed">
|
||
<Sidebar />
|
||
</el-aside>
|
||
<el-main class="">
|
||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||
<div class="panel-shell 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" class="w-full sm:w-auto touch-target">选择图片并上传</el-button>
|
||
</el-upload>
|
||
</div>
|
||
<div class="panel-shell p-6">
|
||
<div class="text-lg font-semibold mb-4">结果集</div>
|
||
<div class="hidden sm:block">
|
||
<el-form :inline="true" class="mb-4">
|
||
<el-form-item label="班级">
|
||
<el-select v-model="classId" placeholder="选择班级" clearable filterable
|
||
@change="onClassChange" style="min-width: 220px">
|
||
<el-option v-for="item in classOptions" :key="item.id"
|
||
:label="`${item.title}${item.gradeName ? '(' + item.gradeName + ')' : ''}`"
|
||
:value="item.id" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="年级">
|
||
<el-select v-model="gradeId" placeholder="选择年级" clearable filterable
|
||
style="min-width: 220px">
|
||
<el-option v-for="g in gradeOptions" :key="g.id" :label="g.title"
|
||
:value="g.id" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="学生姓名">
|
||
<el-input v-model="studentName" placeholder="学生姓名" clearable />
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||
<el-button @click="handleReset">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</div>
|
||
<div class="hidden sm:block overflow-x-auto">
|
||
<el-table :data="list" border class="min-w-[800px]" v-loading="loading"
|
||
@row-click="handleRowClick">
|
||
<el-table-column prop="studentName" label="学生姓名" min-width="120" />
|
||
<el-table-column prop="examWordsTitle" label="试题名称" min-width="160" />
|
||
<el-table-column prop="correctWordCount" label="正确词数" width="120" />
|
||
<el-table-column prop="wrongWordCount" label="错误词数" width="120" />
|
||
<el-table-column label="完成状态" width="120">
|
||
<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">
|
||
<template #default="{ row }">
|
||
{{ row.startDate.replace('T', ' ') }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="msg" label="判卷结算" min-width="180" />
|
||
</el-table>
|
||
</div>
|
||
<div class="sm:hidden space-y-3">
|
||
<div class="mb-3 grid grid-cols-1 gap-3">
|
||
<el-select v-model="classId" placeholder="选择班级" clearable filterable @change="onClassChange" />
|
||
<el-select v-model="gradeId" placeholder="选择年级" clearable filterable />
|
||
<el-input v-model="studentName" placeholder="学生姓名" clearable />
|
||
<div class="flex gap-2">
|
||
<el-button type="primary" class="flex-1" @click="handleSearch">查询</el-button>
|
||
<el-button class="flex-1" @click="handleReset">重置</el-button>
|
||
</div>
|
||
</div>
|
||
<div v-for="row in list" :key="row.id" class="panel-shell p-4">
|
||
<div class="text-base font-semibold mb-1">{{ row.studentName }}</div>
|
||
<div class="text-sm mb-1">试题:{{ row.examWordsTitle }}</div>
|
||
<div class="text-sm mb-1">正确:{{ row.correctWordCount }},错误:{{ row.wrongWordCount }}</div>
|
||
<div class="text-sm mb-1">开始:{{ row.startDate.replace('T', ' ') }}</div>
|
||
<div class="text-sm mb-2">结算:{{ row.msg }}</div>
|
||
<div class="flex items-center justify-between">
|
||
<el-tag :type="row.isFinished === 1 ? 'success' : 'info'">
|
||
{{ row.isFinished === 1 ? '已完成' : '未完成' }}
|
||
</el-tag>
|
||
<el-button size="small" type="primary" @click="handleRowClick(row)">查看详情</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<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>
|
||
<ExamWordsDetailCard v-model="showDetail" :id="selectedId" />
|
||
</el-main>
|
||
</el-container>
|
||
</el-container>
|
||
</div>
|
||
</template>
|
||
|
||
|
||
<script setup>
|
||
import Header from '@/layouts/components/Header.vue'
|
||
import ExamWordsDetailCard from '@/layouts/components/ExamWordsDetailCard.vue'
|
||
import { ref, onMounted } from 'vue'
|
||
import { uploadExamWordsPng, getExamWordsResult } from '@/api/exam'
|
||
import { getClassList } from '@/api/class'
|
||
import { getGradeList } from '@/api/grade'
|
||
import Sidebar from '@/layouts/components/Sidebar.vue'
|
||
|
||
const list = ref([])
|
||
const pageNo = ref(1)
|
||
const pageSize = ref(10)
|
||
const totalCount = ref(0)
|
||
const loading = ref(false)
|
||
const showDetail = ref(false)
|
||
const selectedId = ref(null)
|
||
const classId = ref(null)
|
||
const gradeId = ref(null)
|
||
const studentName = ref('')
|
||
const classOptions = ref([])
|
||
const gradeOptions = ref([])
|
||
|
||
async function fetchList() {
|
||
loading.value = true
|
||
try {
|
||
const res = await getExamWordsResult(
|
||
pageNo.value,
|
||
pageSize.value,
|
||
classId.value,
|
||
gradeId.value,
|
||
studentName.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
|
||
}
|
||
}
|
||
|
||
async function fetchClassOptions() {
|
||
const res = await getClassList(1, 200)
|
||
const d = res.data
|
||
classOptions.value = Array.isArray(d.data) ? d.data : []
|
||
}
|
||
|
||
async function fetchGradeOptions() {
|
||
const res = await getGradeList(1, 200)
|
||
const d = res.data
|
||
gradeOptions.value = Array.isArray(d.data) ? d.data : []
|
||
}
|
||
|
||
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()
|
||
}
|
||
|
||
function handleRowClick(row) {
|
||
selectedId.value = row.id
|
||
showDetail.value = true
|
||
}
|
||
|
||
function handleSearch() {
|
||
pageNo.value = 1
|
||
fetchList()
|
||
}
|
||
|
||
function handleReset() {
|
||
classId.value = null
|
||
gradeId.value = null
|
||
studentName.value = ''
|
||
pageNo.value = 1
|
||
fetchList()
|
||
}
|
||
|
||
function onClassChange(val) {
|
||
const item = classOptions.value.find(i => i.id === val)
|
||
gradeId.value = item ? item.gradeId : null
|
||
}
|
||
|
||
onMounted(() => {
|
||
fetchList()
|
||
fetchClassOptions()
|
||
fetchGradeOptions()
|
||
})
|
||
</script>
|
||
|
||
<style scoped></style>
|