【新增】 1、监控页面支持考试中的考试增加考试时间;2、监控页面更新

This commit is contained in:
dlaren
2025-09-16 22:58:42 +08:00
parent 4ef224fc7a
commit 31d8dd05ef
2 changed files with 172 additions and 50 deletions

View File

@@ -40,6 +40,7 @@ export const MonitorApi = {
deleteMonitor: async (id: number) => { deleteMonitor: async (id: number) => {
return await request.delete({ url: `/exam/monitor/delete?id=` + id }) return await request.delete({ url: `/exam/monitor/delete?id=` + id })
}, },
// 获取学生文件 // 获取学生文件
getMonitorStuFileUrl: async (temporaryId: string) => { getMonitorStuFileUrl: async (temporaryId: string) => {
return await request.get({ url: `/exam/monitor/getMonitorStuFileUrl?temporaryId=` + temporaryId }) return await request.get({ url: `/exam/monitor/getMonitorStuFileUrl?temporaryId=` + temporaryId })
@@ -59,7 +60,17 @@ export const MonitorApi = {
updateMonitorStatus: async (data) => { updateMonitorStatus: async (data) => {
return await request.post({ url: `/exam/monitor/updateMonitorStatus` , data }) return await request.post({ url: `/exam/monitor/updateMonitorStatus` , data })
}, },
getMonitorTaskPage: async (params: any) => { getMonitorTaskPage: async (params: any) => {
return await request.get({ url: `/exam/monitorTask/page`, params }) return await request.get({ url: `/exam/monitorTask/page`, params })
}, },
stopExamForAdmin: async (params: any) => {
return await request.get({ url: `/tool/stopExamForAdmin?userId=`+ params.userId + `&taskId=` + params.taskId + `&paperNum=` + params.paperNum})
},
reStartExamForAdmin: async (data) => {
return await request.post({ url: `/tool/reStartExamForAdmin` , data })
},
} }

View File

@@ -1,21 +1,4 @@
<template> <template>
<!-- 状态选择弹窗 -->
<el-dialog v-model="dialogVisible" title="请选择考试状态" width="400px">
<el-form label-width="80px" class="px-4 pt-2">
<el-form-item label="考试状态">
<el-radio-group v-model="selectedStatus">
<el-radio :label="'0'">待考</el-radio>
<el-radio :label="'1'">考试中</el-radio>
<el-radio :label="'2'">考试完成</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmChange">确认</el-button>
</template>
</el-dialog>
<el-dialog <el-dialog
v-model="initDialogVisible" v-model="initDialogVisible"
title="试卷任务监控列表" title="试卷任务监控列表"
@@ -112,9 +95,9 @@
<el-select v-model="queryParams.taskName" placeholder="请选择试卷任务" class="!w-240px"> <el-select v-model="queryParams.taskName" placeholder="请选择试卷任务" class="!w-240px">
<el-option <el-option
v-for="item in taskList" v-for="item in taskList"
:key="item.taskName" :key="item?.taskName ?? ''"
:label="item.taskName" :label="item?.taskName ?? ''"
:value="item.taskName" :value="item?.taskName ?? ''"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
@@ -188,27 +171,18 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="试卷任务" align="center" prop="taskName" /> <el-table-column label="试卷任务" align="center" prop="taskName" />
<el-table-column label="机器ip" align="center" prop="ip" /> <el-table-column label="机器ip" align="center" prop="ip" width="180px"/>
<el-table-column <el-table-column
label="剩余时间" label="剩余时间"
align="center" align="center"
prop="remainingTime" prop="remainingTime"
:formatter="dateFormatterMin" :formatter="dateFormatterMin"
width="180px"
/>
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
/>
<el-table-column label="操作" align="center" min-width="120px"> <el-table-column label="操作" align="center" min-width="120px">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="downloadFile(scope.row)"> <el-button link type="primary" @click="getMonitorInfo(scope.row)"> 详情 </el-button>
下载 <el-button link type="primary" @click="downloadFile(scope.row)"> 下载 </el-button>
</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -220,7 +194,112 @@
@pagination="getList" @pagination="getList"
/> />
</ContentWrap> </ContentWrap>
<!-- 状态选择弹窗 -->
<el-dialog v-model="dialogVisible" title="参数设置" width="400px" center>
<strong style="color: chocolate"
>*只有状态为考试中的考试才可以增加时间增加的时间的同时并勾选考试中.</strong
>
<el-form label-width="80px" class="px-4 pt-2">
<el-form-item label="考试状态">
<el-radio-group v-model="selectedStatus">
<el-radio :label="'0'">待考</el-radio>
<el-radio :label="'1'">考试中</el-radio>
<el-radio :label="'2'">考试完成</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="增加时长">
<el-time-picker
v-model="times"
value-format="HH:mm:ss"
placeholder="请增加时长"
class="ele-fluid"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmChange">确认</el-button>
</template>
</el-dialog>
<!-- 详情 -->
<el-dialog v-model="dialogVisibleForInfo" title="详情" width="600px" center>
<el-form ref="formRef" :model="formData" label-width="90px">
<el-row>
<el-col :span="12">
<el-form-item label="姓名:" prop="nickname">
{{ formData.nickname }}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="用户名:" prop="username">
{{ formData.username }}
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="班级:" prop="className">
{{ formData.className }}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="试卷任务:" prop="taskName">
{{ formData.taskName }}
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="试卷编号:" prop="paperNum">
{{ formData.paperNum }}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="分数:" prop="score">
{{ formData.score }}
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="剩余时间:" prop="remainingTime">
{{ formData.remainingTime }}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="IP地址" prop="ip">
{{ formData.ip }}
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="开始时间:" prop="startTime">
{{ formData.startTime }}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="结束时间:" prop="endTime">
{{ formData.endTime }}
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="考试状态:" prop="examStatus">
<dict-tag :type="DICT_TYPE.EXAM_STATUS" :value="formData.examStatus" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="任务模式:" prop="taskType">
<dict-tag :type="DICT_TYPE.TASK_TYPE" :value="formData.taskType" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-dialog>
<!-- 表单弹窗添加/修改 --> <!-- 表单弹窗添加/修改 -->
<!-- <MonitorForm ref="formRef" @success="getList" /> --> <!-- <MonitorForm ref="formRef" @success="getList" /> -->
</template> </template>
@@ -233,8 +312,9 @@ import MonitorForm from './MonitorForm.vue'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import taskMonitor from './task-monitor.vue' import taskMonitor from './task-monitor.vue'
import * as ClassApi from '@/api/exam/class' import * as ClassApi from '@/api/exam/class'
const formData = ref<any>({})
import { ref } from 'vue' import { ref } from 'vue'
import { getInfo } from '@/api/login'
const selectedTaskId = ref<string | null>(null) const selectedTaskId = ref<string | null>(null)
const fixedTaskId = ref('') const fixedTaskId = ref('')
@@ -250,9 +330,11 @@ defineOptions({ name: 'Monitor' })
const initDialogVisible = ref(true) const initDialogVisible = ref(true)
// 弹窗开关 // 弹窗开关
const dialogVisible = ref(false) const dialogVisible = ref(false)
const dialogVisibleForInfo = ref(false)
const message = useMessage() // 消息弹窗 const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化 const { t } = useI18n() // 国际化
const selectedStatus = ref<string | null>(null) const selectedStatus = ref<string>('')
const times = ref<string>('')
const loading = ref(true) // 列表的加载中 const loading = ref(true) // 列表的加载中
const list = ref<MonitorVO[]>([]) // 列表的数据 const list = ref<MonitorVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数 const total = ref(0) // 列表的总页数
@@ -260,7 +342,7 @@ const classNameList = ref()
const queryParams = reactive({ const queryParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
monitorId: undefined, monitorId: '',
taskId: '', taskId: '',
username: undefined, username: undefined,
nickname: undefined, nickname: undefined,
@@ -272,7 +354,8 @@ const queryParams = reactive({
taskName: undefined, taskName: undefined,
ip: undefined, ip: undefined,
remainingTime: [], remainingTime: [],
createTime: [] startTime: undefined,
endTime: undefined
}) })
const queryFormRef = ref() // 搜索的表单 const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中 const exportLoading = ref(false) // 导出的加载中
@@ -311,11 +394,15 @@ const returnTop = async () => {
/** 表格选中数据 */ /** 表格选中数据 */
const selections = ref([]) const selections = ref([])
const taskList = ref([]) interface TaskItem {
taskName: string
[key: string]: any
}
const taskList = ref<TaskItem[]>([])
const handleSelectionChange = (rows) => { const handleSelectionChange = (rows) => {
selections.value = rows selections.value = rows
} }
const dateFormatterMin = (row, column, cellValue) => { const dateFormatterMin = (_row: any, _column: any, cellValue: number) => {
if (cellValue == null || isNaN(cellValue)) return '-' if (cellValue == null || isNaN(cellValue)) return '-'
const hours = Math.floor(cellValue / 3600) const hours = Math.floor(cellValue / 3600)
@@ -323,7 +410,7 @@ const dateFormatterMin = (row, column, cellValue) => {
const seconds = cellValue % 60 const seconds = cellValue % 60
// 补零处理 // 补零处理
const pad = (n) => n.toString().padStart(2, '0') const pad = (n: number) => n.toString().padStart(2, '0')
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}` return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`
} }
@@ -347,10 +434,10 @@ const openForm = (type: string, id?: number) => {
} }
// 提交状态变更 // 提交状态变更
const confirmChange = async () => { const confirmChange = async () => {
if (selectedStatus.value === null) { // if (selectedStatus.value === null) {
ElMessage.error('请选择考试状态') // ElMessage.error('请选择考试状态')
return // return
} // }
const rows = selections.value const rows = selections.value
@@ -358,12 +445,31 @@ const confirmChange = async () => {
await MonitorApi.updateMonitorStatus({ await MonitorApi.updateMonitorStatus({
monitorIds, // 这是数组 monitorIds, // 这是数组
status: selectedStatus.value // 这是 0,1,2 status: selectedStatus.value, // 这是 0,1,2
times: times.value // 这是 "HH:mm:ss"
}).then((res) => {
// 清空选择
selections.value = []
selectedStatus.value = ''
times.value = ''
console.log(res)
if (res.type === 'STOP') {
MonitorApi.stopExamForAdmin({
stuId: res.stuId,
taskId: res.taskId,
paperNum: res.paperNum
}) })
} else {
ElMessage.success('考试状态更新成功') MonitorApi.reStartExamForAdmin({
stuId: res.stuId,
taskId: res.taskId,
paperId: res.paperNum
})
}
ElMessage.success('更新成功')
dialogVisible.value = false dialogVisible.value = false
getList() getList()
})
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (id: number) => { const handleDelete = async (id: number) => {
@@ -378,6 +484,11 @@ const handleDelete = async (id: number) => {
} catch {} } catch {}
} }
const getMonitorInfo = async (row: any) => {
formData.value = row
dialogVisibleForInfo.value = true
}
const downloadFile = async (row: any) => { const downloadFile = async (row: any) => {
const res = await MonitorApi.getMonitorStuFileUrl(row.temporaryId) const res = await MonitorApi.getMonitorStuFileUrl(row.temporaryId)
console.log(res) console.log(res)