【新增】 监控管理下载学生文件

This commit is contained in:
dlaren
2025-08-19 16:04:01 +08:00
parent 7ad2ee0b67
commit 1431a96dad
2 changed files with 172 additions and 161 deletions

View File

@@ -40,6 +40,10 @@ export const MonitorApi = {
deleteMonitor: async (id: number) => {
return await request.delete({ url: `/exam/monitor/delete?id=` + id })
},
// 获取学生文件
getMonitorStuFileUrl: async (temporaryId: string) => {
return await request.get({ url: `/exam/monitor/getMonitorStuFileUrl?temporaryId=` + temporaryId })
},
// 导出监控管理 Excel
exportMonitor: async (params) => {

View File

@@ -1,7 +1,6 @@
<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">
@@ -26,14 +25,13 @@
:close-on-click-modal="false"
:show-close="true"
@close="handleNextStepCancel"
>
<taskMonitor @row-clicked="handleTaskSelected" />
>
<taskMonitor @row-clicked="handleTaskSelected" />
<template #footer>
<el-button @click="handleNextStepCancel">取消</el-button>
<el-button type="primary" @click="handleNextStep">下一步</el-button>
</template>
</el-dialog>
</el-dialog>
<ContentWrap>
<!-- 搜索工作栏 -->
@@ -82,10 +80,6 @@
/>
</el-form-item>
<el-form-item label="考试状态" prop="examStatus">
<el-select
v-model="queryParams.examStatus"
@@ -99,9 +93,13 @@
</el-select>
</el-form-item>
<el-form-item label="任务类别" prop="taskType">
<el-select v-model="queryParams.taskType" placeholder="任务类别" class="!w-240px" @change="fetchTaskList">
<el-select
v-model="queryParams.taskType"
placeholder="任务类别"
class="!w-240px"
@change="fetchTaskList"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.TASK_TYPE)"
:key="dict.value"
@@ -139,13 +137,15 @@
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button type="primary" @click="openDialog"><Icon icon="ep:edit" class="mr-5px" />考试状态改变</el-button>
<el-button type="primary" @click="returnTop"><Icon icon="ep:top" class="mr-5px" />返回上级列表</el-button>
<el-button type="primary" @click="openDialog"
><Icon icon="ep:edit" class="mr-5px" />考试状态改变</el-button
>
<el-button type="primary" @click="returnTop"
><Icon icon="ep:top" class="mr-5px" />返回上级列表</el-button
>
<!-- <el-button
type="primary"
plain
@@ -154,24 +154,22 @@
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button> -->
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
>
<el-button type="success" plain @click="handleExport" :loading="exportLoading">
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" @selection-change="handleSelectionChange">
<el-table
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column label="用户账号" align="center" prop="username" />
<el-table-column label="用户姓名" align="center" prop="nickname" />
@@ -205,24 +203,14 @@
: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">
<el-button
link
type="primary"
@click="openForm('update', scope.row.monitorId)"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.monitorId)"
>
删除
<el-button link type="primary" @click="downloadFile(scope.row.temporaryId)">
下载
</el-button>
</template>
</el-table-column> -->
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
@@ -259,24 +247,24 @@ const handleTaskSelected = (taskId: string) => {
/** 监控管理 列表 */
defineOptions({ name: 'Monitor' })
// 新增:初始弹框开关
const initDialogVisible = ref(true);
const initDialogVisible = ref(true)
// 弹窗开关
const dialogVisible = ref(false)
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const selectedStatus = ref<string | null>(null);
const selectedStatus = ref<string | null>(null)
const loading = ref(true) // 列表的加载中
const list = ref<MonitorVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const classNameList = ref();
const classNameList = ref()
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
monitorId:undefined,
taskId:'',
monitorId: undefined,
taskId: '',
username: undefined,
nickname: undefined,
taskType:undefined,
taskType: undefined,
className: undefined,
examStatus: undefined,
score: undefined,
@@ -284,7 +272,7 @@ const queryParams = reactive({
taskName: undefined,
ip: undefined,
remainingTime: [],
createTime: [],
createTime: []
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
@@ -299,7 +287,6 @@ const getList = async () => {
list.value = data.list
total.value = data.total
classNameList.value = await ClassApi.ClassApi.getClassName()
} finally {
loading.value = false
}
@@ -308,41 +295,38 @@ const getList = async () => {
// 打开弹窗
const openDialog = async () => {
const rows = selections.value;
const rows = selections.value
if (!rows.length) {
message.error('请至少选择一条数据');
return;
message.error('请至少选择一条数据')
return
}
dialogVisible.value = true
selections.value = rows;
selections.value = rows
}
const returnTop = async () => {
initDialogVisible.value = true
}
/** 表格选中数据 */
const selections = ref([]);
const taskList = ref([]);
/** 表格选中数据 */
const selections = ref([])
const taskList = ref([])
const handleSelectionChange = (rows) => {
selections.value = rows;
selections.value = rows
}
const dateFormatterMin = (row, column, cellValue) => {
if (cellValue == null || isNaN(cellValue)) return '-';
if (cellValue == null || isNaN(cellValue)) return '-'
const hours = Math.floor(cellValue / 3600);
const minutes = Math.floor((cellValue % 3600) / 60);
const seconds = cellValue % 60;
const hours = Math.floor(cellValue / 3600)
const minutes = Math.floor((cellValue % 3600) / 60)
const seconds = cellValue % 60
// 补零处理
const pad = (n) => n.toString().padStart(2, '0');
const pad = (n) => n.toString().padStart(2, '0')
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
};
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`
}
/** 搜索按钮操作 */
const handleQuery = () => {
@@ -362,28 +346,25 @@ const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
// 提交状态变更
const confirmChange = async () => {
const confirmChange = async () => {
if (selectedStatus.value === null) {
ElMessage.error('请选择考试状态');
return;
ElMessage.error('请选择考试状态')
return
}
const rows = selections.value;
const monitorIds = rows.map((row: any) => row.monitorId);
const rows = selections.value
const monitorIds = rows.map((row: any) => row.monitorId)
await MonitorApi.updateMonitorStatus({
monitorIds, // 这是数组
status: selectedStatus.value // 这是 0,1,2
});
})
ElMessage.success('考试状态更新成功')
dialogVisible.value = false
getList()
}
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
@@ -397,39 +378,66 @@ const handleDelete = async (id: number) => {
} catch {}
}
const selectedRows = ref<string[]>([]);
const downloadFile = async (temporaryId: string) => {
const res = await MonitorApi.getMonitorStuFileUrl(temporaryId)
const url = res
try {
const response = await fetch(temporaryId)
if (!response.ok) {
throw new Error('下载失败')
}
const blob = await response.blob()
const blobUrl = window.URL.createObjectURL(blob)
// 提取文件名
const filename = url.substring(url.lastIndexOf('/') + 1).split('?')[0]
// 创建 a 标签并下载
const a = document.createElement('a')
a.href = blobUrl
a.download = filename
a.style.display = 'none'
document.body.appendChild(a)
a.click()
a.remove()
window.URL.revokeObjectURL(blobUrl)
} catch (err: any) {
ElMessage.error(`下载失败:${err.message}`)
}
}
/** 导出按钮操作 */
const handleExport = async () => {
const rows = selections.value;
const rows = selections.value
if (!rows.length) {
message.error('请至少选择一条数据');
return;
message.error('请至少选择一条数据')
return
}
console.log(rows.length)
console.log(rows.length)
try {
// 导出的二次确认
await message.exportConfirm();
await message.exportConfirm()
// 将选中的 monitorId 填入 queryParams
queryParams.monitorId = rows.map((row: any) => row.monitorId).join(',');
queryParams.monitorId = rows.map((row: any) => row.monitorId).join(',')
exportLoading.value = true;
exportLoading.value = true
const data = await MonitorApi.exportMonitor(queryParams);
download.excel(data, '监控管理.xls');
const data = await MonitorApi.exportMonitor(queryParams)
download.excel(data, '监控管理.xls')
} catch {
// 可选:错误处理
} finally {
exportLoading.value = false;
exportLoading.value = false
// 导出后重置 monitorId避免影响后续查询
queryParams.monitorId = '';
queryParams.monitorId = ''
}
}
const fetchTaskList = async (taskType: string) => {
try {
const res = await MonitorApi.getPaperTaskList( taskType )
const res = await MonitorApi.getPaperTaskList(taskType)
taskList.value = res
} catch (error) {
taskList.value = []
@@ -446,9 +454,8 @@ const handleNextStepCancel = () => {
initDialogVisible.value = false
}
/** 初始化 **/
onMounted(() => {
initDialogVisible.value = true; // 显示初始弹框
initDialogVisible.value = true // 显示初始弹框
})
</script>