【新增】试卷任务前端分类,监控管理前端

This commit is contained in:
YOHO\20373
2025-05-05 00:05:46 +08:00
parent 7a6472d8d4
commit e8a418b9f7
134 changed files with 19516 additions and 493 deletions

View File

@@ -0,0 +1,232 @@
<template>
<Dialog
title="人员分配"
v-model:visible="visible"
width="80%"
append-to-body
lock-scroll
:destroy-on-close="false"
:draggable="false"
@close="handleDialogClose"
>
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
<ele-page style="height: 65vh;">
<el-row :gutter="16" style="height: 100%;">
<!-- 左侧任务列表 -->
<el-col :span="10" style="height: 100%;">
<div style="height: 100%; overflow: auto;">
<ele-card title="试卷任务列表" :body-style="{ paddingTop: '8px' }">
<ContentWrap style="overflow-x: auto;">
<el-table
table-layout="fixed"
ref="taskTableRef"
:data="mockTaskList"
highlight-current-row
@row-click="handleTaskRowClick"
style="min-width: 1000px; width: 100%;"
>
<el-table-column type="index" label="#" width="50" align="center" />
<el-table-column label="考试批次" align="center" prop="batch" />
<el-table-column label="考试开始时间" align="center" prop="startTime" />
<el-table-column label="考试结束时间" align="center" prop="endTime" />
<el-table-column label="允许入场(分钟)" align="center" prop="allowEntry" />
<el-table-column label="禁止入场(分钟)" align="center" prop="endAllowEntry" />
<el-table-column label="允许交卷(分钟)" align="center" prop="allowSubmission" />
</el-table>
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
</ele-card>
</div>
</el-col>
<!-- 右侧人员分配列表 -->
<el-col :span="14" style="height: 100%;">
<div style="height: 100%; overflow: auto;">
<ele-card :body-style="{ paddingTop: '8px' }">
<div class="mb-2">
<el-button type="primary" @click="openAssignDialog()">分配人员</el-button>
<el-button type="danger" @click="removeBatch()">删除</el-button>
<el-button @click="exportData()">导出</el-button>
</div>
<ContentWrap style="overflow-x: auto;">
<el-table
table-layout="fixed"
ref="personTableRef"
:data="filteredPersonList"
style="min-width: 1000px; width: 100%"
highlight-current-row
@selection-change="selections = $event"
>
<el-table-column type="selection" width="50" align="center" />
<el-table-column type="index" label="#" width="50" align="center" />
<el-table-column label="用户名称" align="center" prop="username" />
<el-table-column label="用户昵称" align="center" prop="nickname" />
<el-table-column label="班级名称" align="center" prop="className" />
<el-table-column label="分配考场" align="center" prop="batch" />
</el-table>
<Pagination
:total="stutotal"
v-model:page="stuQuery.pageNo"
v-model:limit="stuQuery.pageSize"
@pagination="getPersonList"
/>
</ContentWrap>
<el-empty
v-if="filteredPersonList.length === 0"
description="暂无分配人员"
/>
</ele-card>
</div>
</el-col>
</el-row>
</ele-page>
<PersonSearch
:session-id="selectedSessionId"
:session-batch="selectedSessionBanch"
:task-id="props.taskId"
v-model="showPersonEdit"
ref="stuAddRef"
@done="reload"
/>
</el-form>
</Dialog>
</template>
<script setup>
import { ref, onMounted, nextTick, reactive } from 'vue';
import PersonSearch from './person-serach.vue';
import * as SmsChannelApi from '@/api/system/session';
import * as PersonApi from '@/api/system/person';
const visible = ref(true);
const selectedSessionId = ref(null);
const selectedSessionBanch = ref(null);
const selections = ref([]);
const showEdit = ref(false);
const current = ref(null);
const taskTableRef = ref();
const personTableRef = ref();
const stuAddRef = ref();
const showPersonEdit = ref(false);
const emit = defineEmits(['done']);
const props = defineProps({
taskId: {
type: String,
default: ''
}
});
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
status: '0',
taskId: props.taskId,
sessionId: ''
});
const stuQuery = reactive({
pageNo: 1,
pageSize: 10
});
const mockTaskList = ref([]);
const total = ref(0);
const filteredPersonList = ref([]);
const stutotal = ref(0);
const openAssignDialog = () => {
if (!selectedSessionId.value) {
ElMessage.warning('请先选择左侧任务!');
return;
}
showPersonEdit.value = true;
nextTick(() => {
stuAddRef.value?.open();
});
};
const handleTaskRowClick = async (row) => {
selectedSessionId.value = row.sessionId;
selectedSessionBanch.value = row.batch;
queryParams.sessionId = row.sessionId;
stuQuery.pageNo = 1; // 重置分页
await getPersonList();
};
const getList = async () => {
const res = await SmsChannelApi.pageSessions(queryParams);
mockTaskList.value = res.list;
total.value = res.total;
};
const getPersonList = async () => {
const personParams = {
...stuQuery,
taskId: props.taskId,
sessionId: selectedSessionId.value
};
const res = await SmsChannelApi.getSessionStu(personParams);
filteredPersonList.value = res.list;
stutotal.value = res.total;
};
const removeBatch = async () => {
const selectedIds = selections.value.map(item => item.id);
if (selectedIds.length === 0) {
ElMessage.warning('请选择要删除的人员');
return;
}
await ElMessageBox.confirm(
'确定要删除选中的人员吗?',
'警告',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
);
const data = {
studentIds: selectedIds,
sessionId: selectedSessionId.value,
taskId: props.taskId,
batch: selectedSessionBanch.value
};
await PersonApi.removeSessionStu(data);
await getPersonList(); // 删除后刷新右侧列表
emit('done');
ElMessage.success('删除成功');
};
const exportData = () => {
console.log('导出功能触发');
};
const handleDialogClose = () => {
visible.value = false;
};
const reload = () => {
getList();
if (selectedSessionId.value) {
getPersonList();
}
emit('done');
};
onMounted(() => {
getList();
});
</script>

View File

@@ -0,0 +1,331 @@
<template>
<!-- 搜索 -->
<Dialog
title="人员分配"
v-model="visible"
width="80%"
append-to-body
lock-scroll
:destroy-on-close="false"
:draggable="false"
@close="handleDialogClose"
>
<ContentWrap>
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="学生名称" prop="username">
<el-input
v-model="queryParams.username"
placeholder="请输入学生名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="班级" prop="className">
<el-select
v-model="queryParams.className"
filterable
allow-create
default-first-option
placeholder="请选择或输入"
style="width: 300px"
>
<el-option
v-for="item in classNameList"
:key="item.className"
:label="item.className"
:value="item.className"
/>
</el-select>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select
v-model="queryParams.status"
placeholder="请选择学生状态"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="datetimerange"
start-placeholder="开始日期"
end-placeholder="结束日期"
class="!w-240px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
<!-- <el-button
type="primary"
plain
@click="openForm('create')"
>
<Icon icon="ep:plus" /> 新增
</el-button> -->
<!-- <el-button
type="warning"
plain
@click="handleImport"
>
<Icon icon="ep:upload" /> 导入
</el-button> -->
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
>
<Icon icon="ep:download" />导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<ContentWrap>
<el-table v-loading="loading" ref="tableRef" :data="list" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="用户编号" align="center" key="id" prop="id" />
<el-table-column
label="用户名称"
align="center"
prop="username"
:show-overflow-tooltip="true"
/>
<el-table-column
label="用户昵称"
align="center"
prop="nickname"
:show-overflow-tooltip="true"
/>
<el-table-column
label="班级名称"
align="center"
prop="className"
:show-overflow-tooltip="true"
/>
<el-table-column label="状态" key="status" align="center">
<template #default="scope">
<el-switch
v-model="scope.row.status"
:active-value="0"
:inactive-value="1"
disabled
/>
</template>
</el-table-column>
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180"
/>
</el-table>
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="open"
/>
</ContentWrap>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleDialogClose">取消</el-button>
<el-button type="primary" @click="confirmSelection">确认</el-button>
</span>
</template>
</Dialog>
<!-- 用户导入对话框 -->
<!-- <StudentImportForm ref="importFormRef" @success="open" /> -->
</template>
<script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import * as UserApi from '@/api/system/user'
import * as ClassApi from '@/api/exam/class'
import * as PersonApi from '@/api/system/person'
defineOptions({ name: 'SystemUser' })
const props = defineProps({
sessionId: {
type: String,
default: ''
},
taskId: {
type: String,
default: ''
},
sessionBatch:{
type: String,
default: ''
}
})
const tableRef = ref()
const visible = defineModel({ type: Boolean });
const classNameList = ref();
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中
const total = ref(0) // 列表的总页数
const list = ref([]) // 列表的数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
className: undefined,
nickname: undefined,
username: undefined,
mobile: undefined,
status: undefined,
deptId: undefined,
userType: "2",
createTime: [],
taskId:'',
})
const queryFormRef = ref() // 搜索的表单
/** 查询列表 */
const open = async () => {
loading.value = true
classNameList.value = await ClassApi.ClassApi.getClassName()
queryParams.taskId=props.taskId
console.log(props.taskId+"props.taskId")
console.log(props.sessionId+"props.sessionId")
try {
const params: any = {
...queryParams,
sessionId:props.sessionId,
startTime: queryParams.createTime?.[0] || null,
endTime: queryParams.createTime?.[1] || null,
}
const data = await PersonApi.getSessionStuBySearch(params)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
const emit = defineEmits(['done'])
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
open()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 用户导入 */
const importFormRef = ref()
const handleImport = () => {
importFormRef.value.open()
}
const multipleSelection = ref<UserApi.UserVO[]>([])
const handleSelectionChange = (val: UserApi.UserVO[]) => {
multipleSelection.value = val
}
/** 导出按钮操作 */
const exportLoading = ref(false)
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await UserApi.exportUser(queryParams)
download.excel(data, '用户数据.xls')
} catch {
} finally {
exportLoading.value = false
}
}
const handleDialogClose = () => {
visible.value = false;
multipleSelection.value = [] // 清空选中的数据
tableRef.value?.clearSelection()
};
const confirmSelection = async () => {
const selectedIds = multipleSelection.value.map(item => item.id)
const count = selectedIds.length
if (count === 0) {
ElMessage.warning('请先选择学生')
return
}
try {
await ElMessageBox.confirm(
`已选中 ${count} 个学生,是否确认分配?`,
'确认分配',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}
)
const data = {
studentIds: selectedIds,
sessionId: props.sessionId,
taskId: props.taskId,
batch:props.sessionBatch
}
await PersonApi.setSessionStu(data)
ElMessage.success(`成功分配 ${count} 个学生`)
emit('done')
visible.value = false
multipleSelection.value = []
tableRef.value?.clearSelection()
} catch (err) {
// 用户取消时会进入这里,不需要处理
console.log('用户取消了分配')
}
}
defineExpose({ open })
/** 初始化 */
onMounted(() => {
open()
})
</script>

View File

@@ -0,0 +1,250 @@
<template>
<ContentWrap>
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<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"
plain
@click="openSearch('create')"
v-hasPermi="['system:sms-channel:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 添加学生</el-button
>
<el-button
v-if="showAssignButton"
type="primary"
plain
@click="openEdit('create')"
v-hasPermi="['system:sms-channel:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 分配场次</el-button
>
<el-button
type="danger"
class="ele-btn-del"
:disabled="!selections.length"
@click="handleDeletes()"
>
批量删除
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" @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" />
<el-table-column label="班级名称" align="center" prop="className"/>
<el-table-column label="分配考场" align="center" prop="batch"/>
<!-- <el-table-column label="操作" align="center">
<template #default="scope">
<el-button
link
type="primary"
@click="openEdit('update', scope.row)"
v-hasPermi="['system:sms-channel:update']"
>
查看
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.schemeId)"
v-hasPermi="['system:sms-channel:delete']"
>
删除
</el-button>
</template>
</el-table-column> -->
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 编辑弹窗 -->
<person-edit v-model="showEdit" :task-Id="props.taskId" @done="reload" />
<PersonSearch :task-id="props.taskId" v-model="showPersonEdit" ref="stuAddRef" @done="reload" />
</template>
<script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import * as SmsPersonlApi from '@/api/system/person';
import PersonEdit from './components/person-edit.vue';
import PersonSearch from './components/person-serach.vue'
import * as SmsChannelApi from '@/api/system/session';
import { fa } from 'element-plus/es/locale';
defineOptions({ name: 'SystemSmsChannel' })
const showAssignButton = ref(true) // 控制按钮显示
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const props = defineProps({
taskSpecialty: {
type: String,
default: ''
},
taskId: {
type: String,
default: ''
},
formData:{
type: Object,
default: null
}
})
const showPersonEdit = ref(false)
/** 当前编辑数据 */
const current = ref<object>();
/** 是否显示编辑弹窗 */
const showEdit = ref(false);
const stuAddRef = ref();
const taskEditRef = ref()
const loading = ref(false) // 列表的加载中
const total = ref(0) // 列表的总页数
const list = ref([]) // 列表的数据
const queryFormRef = ref() // 搜索的表单
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
signature: undefined,
status: undefined,
createTime: [],
taskId: props.taskId
})
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const resButton = await SmsChannelApi.fetchNoMes(props.taskId)
if (resButton==='200') {
showAssignButton.value = true
} else {
showAssignButton.value = false
}
const res = await SmsPersonlApi.pagePersons(queryParams)
console.log(res)
list.value = res.list
total.value = res.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
const reload = () => {
getList()
}
/** 表格选中数据 */
const selections = ref([]);
const handleSelectionChange = (rows) => {
selections.value = rows;
}
const selectedRows = ref<string[]>([]);
const handleDeletes = async () => {
try {
const rows = selections.value;
if (!rows.length) {
message.error('请至少选择一条数据');
return;
}
selectedRows.value = rows.map((d: any) => d.id); // 保存选中的行数据
const deleteData = {
studentIds: selectedRows.value, // 选中的 ID 列表
taskId: props.taskId // 任务 ID
}
await SmsPersonlApi.removePersons(deleteData)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
const openEdit = (type: string, row?: object) => {
showEdit.value = true
current.value = row
nextTick(() => {
taskEditRef.value?.open(type, row)
})
}
const openSearch = (type: string, row?: object) => {
showPersonEdit.value = true
current.value = row
nextTick(() => {
stuAddRef.value?.open(type, row)
})
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
console.log(id+"idididid")
await SmsPersonlApi.removePerson(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>