完善分班部分
This commit is contained in:
parent
fe2a4ebcfa
commit
eb72b0467d
|
|
@ -0,0 +1,11 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 自动分班
|
||||||
|
export function authclass(params?: Record<string, any>) {
|
||||||
|
return request.post({ url: '/class/assign/authclass', params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认分班
|
||||||
|
export function confirm(params?: Record<string, any>) {
|
||||||
|
return request.post({ url: '/class/assign/confirm', params })
|
||||||
|
}
|
||||||
|
|
@ -64,3 +64,8 @@ export function stuRegistrationEnrollment(params: Record<string, any>) {
|
||||||
export function stuRegistrationPayment(params: Record<string, any>) {
|
export function stuRegistrationPayment(params: Record<string, any>) {
|
||||||
return request.post({ url: '/stuRegistration/op/payment', params })
|
return request.post({ url: '/stuRegistration/op/payment', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加随机学生信息
|
||||||
|
export function stuGenreginfo(params: Record<string, any>) {
|
||||||
|
return request.post({ url: '/stuRegistration/genreginfo', params })
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,275 @@
|
||||||
|
<template>
|
||||||
|
<!-- 模板部分保持不变 -->
|
||||||
|
<div class="class-popup">
|
||||||
|
<popup
|
||||||
|
ref="popupRef"
|
||||||
|
:title="popupTitle"
|
||||||
|
:async="true"
|
||||||
|
width="800px"
|
||||||
|
:clickModalClose="true"
|
||||||
|
@confirm="handleConfirm"
|
||||||
|
@close="handleClose"
|
||||||
|
>
|
||||||
|
<el-card class="!border-none" shadow="never">
|
||||||
|
<el-form ref="formRef" :model="queryParams" :inline="true">
|
||||||
|
<el-form-item label="班级代码" prop="classCode">
|
||||||
|
<el-input v-model="queryParams.classCode" class="w-[200px]" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="班级名称" prop="className">
|
||||||
|
<el-input v-model="queryParams.className" class="w-[200px]" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="入学年份" prop="enrollmentYear">
|
||||||
|
<el-input v-model="queryParams.enrollmentYear" class="w-[200px]" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="所属学院" prop="collegeId">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.collegeId"
|
||||||
|
class="w-[200px]"
|
||||||
|
clearable
|
||||||
|
@change="handleCollegeChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="college in collegeList"
|
||||||
|
:key="college.id"
|
||||||
|
:value="college.id"
|
||||||
|
:label="college.collegeName"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="getClassLists">查询</el-button>
|
||||||
|
<el-button @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-table
|
||||||
|
ref="tableRef"
|
||||||
|
:data="pager.lists"
|
||||||
|
size="small"
|
||||||
|
v-loading="pager.loading"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
>
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
<el-table-column prop="classCode" label="班级代码" min-width="120" />
|
||||||
|
<el-table-column prop="className" label="班级名称" min-width="150" />
|
||||||
|
<el-table-column prop="collegeName" label="所属学院" min-width="150" />
|
||||||
|
<el-table-column prop="majorName" label="所属专业" min-width="150" />
|
||||||
|
<el-table-column
|
||||||
|
prop="studentCount"
|
||||||
|
label="学生人数"
|
||||||
|
min-width="100"
|
||||||
|
:formatter="(row: { studentCount: any; maxStudentCount: any }) => `${row.studentCount}/${row.maxStudentCount}`"
|
||||||
|
:cell-style="({ row }: { row: { studentCount: any; maxStudentCount: any } }) => {
|
||||||
|
// 判断:当人数相等时,字体变红
|
||||||
|
return row.studentCount === row.maxStudentCount
|
||||||
|
? { color: 'red' }
|
||||||
|
: {}
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div class="flex justify-end mt-4">
|
||||||
|
<pagination v-model="pager" @change="getClassLists" />
|
||||||
|
</div>
|
||||||
|
</popup>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup name="ClassPopup">
|
||||||
|
import type { PropType } from 'vue'
|
||||||
|
import { nextTick, reactive, ref, shallowRef, watch } from 'vue'
|
||||||
|
|
||||||
|
import { classLists } from '@/api/class'
|
||||||
|
import { collegeLists } from '@/api/college'
|
||||||
|
import Popup from '@/components/popup/index.vue'
|
||||||
|
import { usePaging } from '@/hooks/usePaging'
|
||||||
|
import feedback from '@/utils/feedback'
|
||||||
|
|
||||||
|
// 外部传入的已选择班级ID
|
||||||
|
const props = defineProps({
|
||||||
|
selectedIds: {
|
||||||
|
type: Array as PropType<number[]>,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 向父组件传递事件
|
||||||
|
const emit = defineEmits(['success', 'close'])
|
||||||
|
|
||||||
|
// 弹窗相关
|
||||||
|
const popupRef = shallowRef<InstanceType<typeof Popup>>()
|
||||||
|
const popupTitle = ref('选择班级') // 统一弹窗标题
|
||||||
|
|
||||||
|
// 表格引用
|
||||||
|
const tableRef = shallowRef<any>(null)
|
||||||
|
|
||||||
|
// 查询参数
|
||||||
|
const queryParams = reactive({
|
||||||
|
classCode: '',
|
||||||
|
className: '',
|
||||||
|
collegeId: '',
|
||||||
|
enrollmentYear: '',
|
||||||
|
maxStudentCount: '',
|
||||||
|
majorId: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 学院列表
|
||||||
|
const collegeList = ref<any[]>([])
|
||||||
|
|
||||||
|
// 存储与父组件交互的选中班级ID(外部数据)
|
||||||
|
const allSelectedId = ref<number[]>([])
|
||||||
|
// 存储组件内部使用的选中班级ID(内部状态)
|
||||||
|
const allSelectedIds = ref<number[]>([])
|
||||||
|
|
||||||
|
// 分页配置
|
||||||
|
const { pager, getLists: getClassLists } = usePaging({
|
||||||
|
fetchFun: classLists,
|
||||||
|
params: queryParams
|
||||||
|
})
|
||||||
|
|
||||||
|
// 加载学院列表
|
||||||
|
const fetchColleges = async () => {
|
||||||
|
try {
|
||||||
|
const res = await collegeLists()
|
||||||
|
collegeList.value = res.lists
|
||||||
|
} catch (err) {
|
||||||
|
feedback.msgError('获取学院列表失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 学院变更时重新加载专业
|
||||||
|
const handleCollegeChange = async () => {
|
||||||
|
getClassLists()
|
||||||
|
// 实际项目中可能需要加载对应学院的专业
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理选择变化 - 只修改内部状态
|
||||||
|
const handleSelectionChange = (rows: any[]) => {
|
||||||
|
const currentPageSelectedIds = rows.map((row) => row.id)
|
||||||
|
const currentPageIds = pager.lists.map((row) => row.id)
|
||||||
|
// 移除当前页中取消选中的ID
|
||||||
|
currentPageIds.forEach((id) => {
|
||||||
|
if (!currentPageSelectedIds.includes(id)) {
|
||||||
|
const index = allSelectedIds.value.indexOf(id)
|
||||||
|
if (index > -1) {
|
||||||
|
allSelectedIds.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 添加当前页中新选中的ID
|
||||||
|
currentPageSelectedIds.forEach((id) => {
|
||||||
|
if (!allSelectedIds.value.includes(id)) {
|
||||||
|
allSelectedIds.value.push(id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置查询
|
||||||
|
const resetQuery = () => {
|
||||||
|
Object.keys(queryParams).forEach((key) => {
|
||||||
|
queryParams[key as keyof typeof queryParams] = ''
|
||||||
|
})
|
||||||
|
getClassLists()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开弹窗时初始化选中状态
|
||||||
|
const initSelection = async () => {
|
||||||
|
await nextTick()
|
||||||
|
if (!tableRef.value || !pager.lists || pager.lists.length === 0) {
|
||||||
|
console.warn('表格数据未加载完成')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// 先清空所有选中状态
|
||||||
|
tableRef.value.clearSelection()
|
||||||
|
// 遍历当前页数据,设置选中状态(使用外部状态)
|
||||||
|
pager.lists.forEach((row: any) => {
|
||||||
|
if (allSelectedId.value.includes(row.id)) {
|
||||||
|
nextTick(() => {
|
||||||
|
tableRef.value?.toggleRowSelection(row, true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('初始化选中状态失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认选择 - 同步内部状态到外部数据并传递给父组件
|
||||||
|
const handleConfirm = async () => {
|
||||||
|
if (allSelectedIds.value.length === 0) {
|
||||||
|
return feedback.msgWarning('请选择班级')
|
||||||
|
}
|
||||||
|
// 同步内部状态到外部数据
|
||||||
|
allSelectedId.value = [...allSelectedIds.value]
|
||||||
|
emit('success', allSelectedId.value)
|
||||||
|
popupRef.value?.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭弹窗 - 不保存修改,保持原有外部数据
|
||||||
|
const handleClose = () => {
|
||||||
|
// 关闭时恢复内部状态与外部数据一致
|
||||||
|
allSelectedIds.value = [...allSelectedId.value]
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开弹窗方法 - 从父组件传入值同步到内部状态
|
||||||
|
const open = async () => {
|
||||||
|
// 同步父组件传入的值到外部数据
|
||||||
|
allSelectedId.value = [...props.selectedIds]
|
||||||
|
// 同步外部数据到内部状态
|
||||||
|
allSelectedIds.value = [...allSelectedId.value]
|
||||||
|
try {
|
||||||
|
await getClassLists()
|
||||||
|
popupRef.value?.open()
|
||||||
|
await nextTick()
|
||||||
|
setTimeout(() => {
|
||||||
|
initSelection()
|
||||||
|
}, 100)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('打开弹窗失败:', error)
|
||||||
|
}
|
||||||
|
fetchColleges()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听外部选中ID变化
|
||||||
|
watch(
|
||||||
|
() => props.selectedIds,
|
||||||
|
(val) => {
|
||||||
|
if (!popupRef.value?.visible) {
|
||||||
|
allSelectedId.value = [...val]
|
||||||
|
allSelectedIds.value = [...allSelectedId.value]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// 监听表格数据变化,数据加载完成后自动设置选中状态
|
||||||
|
watch(
|
||||||
|
() => pager.lists,
|
||||||
|
(newLists) => {
|
||||||
|
if (newLists && newLists.length > 0 && popupRef.value?.visible) {
|
||||||
|
setTimeout(() => {
|
||||||
|
initSelection()
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// 监听页码变化
|
||||||
|
watch(
|
||||||
|
() => pager.page,
|
||||||
|
() => {
|
||||||
|
if (popupRef.value?.visible) {
|
||||||
|
allSelectedId.value = [...allSelectedIds.value]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 暴露open方法供父组件调用
|
||||||
|
defineExpose({
|
||||||
|
open
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
<template>
|
||||||
|
<div class="index-lists">
|
||||||
|
<el-card class="!border-none" shadow="never">
|
||||||
|
<el-form ref="formRef" class="mb-[-16px]" :model="queryParams" :inline="true">
|
||||||
|
<el-form-item label="班级代码" prop="classCode">
|
||||||
|
<el-input class="w-[280px]" v-model="queryParams.classCode" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="班级名称" prop="className">
|
||||||
|
<el-input class="w-[280px]" v-model="queryParams.className" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="所属学院" prop="collegeId">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.collegeId"
|
||||||
|
class="w-[280px]"
|
||||||
|
@change="fetchMajorLists()"
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="college in getCollegeLists.lists"
|
||||||
|
:key="college.id"
|
||||||
|
:value="college.id"
|
||||||
|
:label="college.collegeName"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="所属专业" prop="majorId">
|
||||||
|
<el-select v-model="queryParams.majorId" class="w-[280px]" clearable>
|
||||||
|
<el-option
|
||||||
|
v-for="college in getMajorLists.lists"
|
||||||
|
:key="college.id"
|
||||||
|
:value="college.id"
|
||||||
|
:label="college.majorName"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="班主任用户ID" prop="headTeacherId">
|
||||||
|
<el-input class="w-[280px]" v-model="queryParams.headTeacherId" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="班级状态" prop="classStatus">
|
||||||
|
<el-select v-model="queryParams.classStatus" class="w-[280px]" clearable>
|
||||||
|
<el-option label="在读" :value="1" />
|
||||||
|
<el-option label="毕业" :value="2" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="resetPage">查询</el-button>
|
||||||
|
<el-button @click="resetParams">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
<el-card class="!border-none mt-4" shadow="never">
|
||||||
|
<div>
|
||||||
|
<el-button v-perms="['class:add']" type="primary" @click="handleAdd()">
|
||||||
|
<template #icon>
|
||||||
|
<icon name="el-icon-Plus" />
|
||||||
|
</template>
|
||||||
|
新增
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table class="mt-4" size="large" v-loading="pager.loading" :data="pager.lists">
|
||||||
|
<el-table-column label="学院名称" prop="collegeName" min-width="100" />
|
||||||
|
<el-table-column label="专业名称" prop="majorName" min-width="100" />
|
||||||
|
<el-table-column label="班级代码" prop="classCode" min-width="100" />
|
||||||
|
<el-table-column label="班级名称" prop="className" min-width="100" />
|
||||||
|
<el-table-column label="学生人数" prop="studentCount" min-width="100" />
|
||||||
|
<el-table-column label="最大学生数" prop="maxStudentCount" min-width="100" />
|
||||||
|
<el-table-column label="入学年份" prop="enrollmentYear" min-width="100" />
|
||||||
|
<el-table-column label="预计毕业年份" prop="graduationYear" min-width="100" />
|
||||||
|
<el-table-column
|
||||||
|
label="班级状态"
|
||||||
|
prop="classStatus"
|
||||||
|
min-width="100"
|
||||||
|
:formatter="formatClassStatus"
|
||||||
|
/>
|
||||||
|
<el-table-column label="创建时间" prop="createTime" min-width="100" />
|
||||||
|
<el-table-column label="更新时间" prop="updateTime" min-width="100" />
|
||||||
|
<el-table-column label="操作" width="120" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button type="primary" link v-perms="['info/list']">
|
||||||
|
<router-link
|
||||||
|
:to="{
|
||||||
|
path: getRoutePath('info/list'),
|
||||||
|
query: {
|
||||||
|
id: row.id,
|
||||||
|
className: row.className
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
学生信息
|
||||||
|
</router-link>
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-perms="['class:edit']"
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="handleEdit(row)"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-perms="['class:del']"
|
||||||
|
type="danger"
|
||||||
|
link
|
||||||
|
@click="handleDelete(row.id)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="flex justify-end mt-4">
|
||||||
|
<pagination v-model="pager" @change="getLists" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
<edit-popup v-if="showEdit" ref="editRef" @success="getLists" @close="showEdit = false" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup name="class">
|
||||||
|
import { classDelete, classLists } from '@/api/class'
|
||||||
|
import { collegeLists } from '@/api/college'
|
||||||
|
import { majorLists } from '@/api/major'
|
||||||
|
import { usePaging } from '@/hooks/usePaging'
|
||||||
|
import { getRoutePath } from '@/router'
|
||||||
|
import feedback from '@/utils/feedback'
|
||||||
|
|
||||||
|
import EditPopup from './edit.vue'
|
||||||
|
const editRef = shallowRef<InstanceType<typeof EditPopup>>()
|
||||||
|
const getCollegeLists = ref<{ lists: Array<{ id: number; collegeName: string }> }>({ lists: [] })
|
||||||
|
const getMajorLists = ref<{ lists: Array<{ id: number; majorName: string }> }>({ lists: [] })
|
||||||
|
const showEdit = ref(false)
|
||||||
|
const route = useRoute()
|
||||||
|
const queryParams = reactive({
|
||||||
|
collegeName: '',
|
||||||
|
majorName: '',
|
||||||
|
classCode: '',
|
||||||
|
className: '',
|
||||||
|
collegeId: '',
|
||||||
|
majorId: route.query.id ? Number(route.query.id) : undefined,
|
||||||
|
headTeacherId: '',
|
||||||
|
enrollmentYear: '',
|
||||||
|
graduationYear: '',
|
||||||
|
classStatus: null
|
||||||
|
})
|
||||||
|
|
||||||
|
const { pager, getLists, resetPage, resetParams } = usePaging({
|
||||||
|
fetchFun: classLists,
|
||||||
|
params: queryParams
|
||||||
|
})
|
||||||
|
|
||||||
|
const fetchCollegeLists = async () => {
|
||||||
|
try {
|
||||||
|
const res = await collegeLists()
|
||||||
|
getCollegeLists.value = res
|
||||||
|
} catch (err) {
|
||||||
|
feedback.msgError('获取学院列表失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchMajorLists = async () => {
|
||||||
|
try {
|
||||||
|
const res = await majorLists(queryParams)
|
||||||
|
getMajorLists.value = res
|
||||||
|
} catch (err) {
|
||||||
|
feedback.msgError('获取专业列表失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatClassStatus = (row: any, column: any, cellValue: any) => {
|
||||||
|
const statusMap: Record<string | number, string> = {
|
||||||
|
'1': '在读',
|
||||||
|
'2': '毕业'
|
||||||
|
}
|
||||||
|
return statusMap[String(cellValue)] || cellValue
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAdd = async () => {
|
||||||
|
showEdit.value = true
|
||||||
|
await nextTick()
|
||||||
|
editRef.value?.open('add')
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEdit = async (data: any) => {
|
||||||
|
showEdit.value = true
|
||||||
|
await nextTick()
|
||||||
|
editRef.value?.open('edit')
|
||||||
|
editRef.value?.getDetail(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDelete = async (id: number) => {
|
||||||
|
await feedback.confirm('确定要删除?')
|
||||||
|
await classDelete({ id })
|
||||||
|
feedback.msgSuccess('删除成功')
|
||||||
|
getLists()
|
||||||
|
}
|
||||||
|
|
||||||
|
getLists()
|
||||||
|
fetchCollegeLists()
|
||||||
|
fetchMajorLists()
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,221 @@
|
||||||
|
<template>
|
||||||
|
<div class="class-divide">
|
||||||
|
<el-card class="!border-none" shadow="never">
|
||||||
|
<el-form ref="formRef" class="mb-[-16px]" :model="formData" :inline="true">
|
||||||
|
<el-form-item label="目标班级" prop="classIds">
|
||||||
|
<!-- 班级选择按钮 -->
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
class="w-[280px] text-left justify-start"
|
||||||
|
@click="openClassPopup"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
formData.classIds.length
|
||||||
|
? '已选择' + formData.classIds.length + '个班级'
|
||||||
|
: '请选择需要分配的班级'
|
||||||
|
}}
|
||||||
|
</el-button>
|
||||||
|
<!-- 仅显示已选择班级数量 -->
|
||||||
|
<div class="selected-count mt-2">
|
||||||
|
已选择 {{ formData.classIds.length }} 个班级
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="待分班学生" prop="studentIds">
|
||||||
|
<!-- 学生选择按钮 -->
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
class="w-[280px] text-left justify-start"
|
||||||
|
@click="openStudentPopup"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
formData.studentIds.length
|
||||||
|
? '已选择' + formData.studentIds.length + '名学生'
|
||||||
|
: '请选择需要分班的学生'
|
||||||
|
}}
|
||||||
|
</el-button>
|
||||||
|
<!-- 仅显示已选择学生数量 -->
|
||||||
|
<div class="selected-count mt-2">
|
||||||
|
已选择 {{ formData.studentIds.length }} 名学生
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="分班模式" prop="model">
|
||||||
|
<el-radio-group v-model="formData.model" class="w-[280px]">
|
||||||
|
<el-radio :label="1">优先填满模式</el-radio>
|
||||||
|
<el-radio :label="2">优先比例平均模式</el-radio>
|
||||||
|
<el-radio :label="3">优先人数平均模式</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="handleDivide">自动分班</el-button>
|
||||||
|
<el-button @click="resetForm">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-card class="!border-none mt-4" shadow="never">
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<h3>分班结果预览</h3>
|
||||||
|
<el-button v-perms="['class:export']" type="primary" plain @click="handleExport">
|
||||||
|
<template #icon>
|
||||||
|
<icon name="el-icon-Download" />
|
||||||
|
</template>
|
||||||
|
导出结果
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table class="mt-4" size="large" v-loading="loading" :data="resultList">
|
||||||
|
<el-table-column label="班级名称" prop="className" min-width="150" />
|
||||||
|
<el-table-column label="学生姓名" prop="studentName" min-width="150" />
|
||||||
|
<el-table-column label="分配方式" prop="modelName" min-width="150" />
|
||||||
|
<el-table-column label="分配时间" prop="assignTime" min-width="180" />
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div class="flex justify-end mt-4" v-if="resultList.length > 0">
|
||||||
|
<pagination v-model="pager" @change="getResultList" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
<!-- 班级弹窗 -->
|
||||||
|
<class-popup
|
||||||
|
ref="classRef"
|
||||||
|
:selected-ids="formData.classIds"
|
||||||
|
@success="handleClassSelect"
|
||||||
|
@close="showClass = false"
|
||||||
|
/>
|
||||||
|
<!-- 学生弹窗 -->
|
||||||
|
<student-popup
|
||||||
|
ref="studentRef"
|
||||||
|
:selected-ids="formData.studentIds"
|
||||||
|
@success="handleStudentSelect"
|
||||||
|
@close="showStudent = false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup name="classDivide">
|
||||||
|
import { reactive, ref, shallowRef } from 'vue'
|
||||||
|
|
||||||
|
import { authclass } from '@/api/assign'
|
||||||
|
import { classLists } from '@/api/class'
|
||||||
|
import { usePaging } from '@/hooks/usePaging'
|
||||||
|
import feedback from '@/utils/feedback'
|
||||||
|
|
||||||
|
import ClassPopup from './class.vue'
|
||||||
|
import StudentPopup from './student.vue'
|
||||||
|
|
||||||
|
// 弹窗控制
|
||||||
|
const showClass = ref(false)
|
||||||
|
const showStudent = ref(false)
|
||||||
|
|
||||||
|
// 弹窗引用
|
||||||
|
const classRef = shallowRef<InstanceType<typeof ClassPopup>>()
|
||||||
|
const studentRef = shallowRef<InstanceType<typeof StudentPopup>>()
|
||||||
|
|
||||||
|
// 打开班级弹窗
|
||||||
|
const openClassPopup = () => {
|
||||||
|
classRef.value?.open()
|
||||||
|
showClass.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开学生弹窗
|
||||||
|
const openStudentPopup = () => {
|
||||||
|
studentRef.value?.open()
|
||||||
|
showStudent.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const formData = reactive({
|
||||||
|
classIds: [] as number[],
|
||||||
|
model: 1,
|
||||||
|
studentIds: [] as number[]
|
||||||
|
})
|
||||||
|
|
||||||
|
// 列表数据
|
||||||
|
const resultList = ref<Array<any>>([])
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
// 处理班级选择结果
|
||||||
|
const handleClassSelect = (selectedIds: number[]) => {
|
||||||
|
formData.classIds = selectedIds
|
||||||
|
showClass.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理学生选择结果
|
||||||
|
const handleStudentSelect = (selectedIds: number[]) => {
|
||||||
|
formData.studentIds = selectedIds
|
||||||
|
showStudent.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页配置
|
||||||
|
const { pager, getLists: getResultList } = usePaging({
|
||||||
|
fetchFun: classLists, // 替换为实际的分班结果接口
|
||||||
|
params: reactive({ page: 1, limit: 10 })
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表单引用
|
||||||
|
const formRef = shallowRef<any>(null)
|
||||||
|
|
||||||
|
// 处理自动分班
|
||||||
|
const handleDivide = async () => {
|
||||||
|
if (!formData.classIds.length) {
|
||||||
|
return feedback.msgWarning('请选择目标班级')
|
||||||
|
}
|
||||||
|
if (!formData.studentIds.length) {
|
||||||
|
return feedback.msgWarning('请选择待分班学生')
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
await authclass(formData)
|
||||||
|
feedback.msgSuccess('分班成功')
|
||||||
|
getResultList() // 重新获取分班结果
|
||||||
|
resetForm() // 重置表单
|
||||||
|
} catch (err) {
|
||||||
|
feedback.msgError('分班失败,请重试')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
|
const resetForm = () => {
|
||||||
|
formData.classIds = []
|
||||||
|
formData.studentIds = []
|
||||||
|
formData.model = 1
|
||||||
|
formRef.value?.resetFields()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出结果
|
||||||
|
const handleExport = () => {
|
||||||
|
feedback.confirm('导出功能开发中')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化加载数据
|
||||||
|
getResultList()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.class-divide {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-count {
|
||||||
|
padding: 4px 8px;
|
||||||
|
background-color: #f0f9ff;
|
||||||
|
border: 1px solid #cce5ff;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 调整表单项样式 */
|
||||||
|
:deep(.el-form-item__content) {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,231 @@
|
||||||
|
<template>
|
||||||
|
<!-- 模板部分保持不变 -->
|
||||||
|
<div class="student-popup">
|
||||||
|
<popup
|
||||||
|
ref="popupRef"
|
||||||
|
:title="popupTitle"
|
||||||
|
:async="true"
|
||||||
|
width="800px"
|
||||||
|
:clickModalClose="true"
|
||||||
|
@confirm="handleConfirm"
|
||||||
|
@close="handleClose"
|
||||||
|
>
|
||||||
|
<el-card class="!border-none" shadow="never">
|
||||||
|
<el-form ref="formRef" :model="queryParams" :inline="true">
|
||||||
|
<el-form-item label="报名编号" prop="applicationNumber">
|
||||||
|
<el-input class="w-[280px]" v-model="queryParams.applicationNumber" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="报名时间" prop="applicationTime">
|
||||||
|
<daterange-picker
|
||||||
|
v-model:startTime="queryParams.applicationTimeStart"
|
||||||
|
v-model:endTime="queryParams.applicationTimeEnd"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="getStudentLists">查询</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-table
|
||||||
|
ref="tableRef"
|
||||||
|
:data="pager.lists"
|
||||||
|
size="small"
|
||||||
|
v-loading="pager.loading"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
>
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
<el-table-column prop="applicationNumber" label="报名编号" min-width="120" />
|
||||||
|
<el-table-column prop="name" label="姓名" min-width="120" />
|
||||||
|
<el-table-column prop="gender" label="性别" min-width="120" />
|
||||||
|
<el-table-column prop="nativePlace" label="籍贯" min-width="120" />
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div class="flex justify-end mt-4">
|
||||||
|
<pagination v-model="pager" @change="getStudentLists" />
|
||||||
|
</div>
|
||||||
|
</popup>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup name="StudentPopup">
|
||||||
|
import type { PropType } from 'vue'
|
||||||
|
import { nextTick, reactive, ref, shallowRef, watch } from 'vue'
|
||||||
|
|
||||||
|
import { collegeLists } from '@/api/college'
|
||||||
|
import { stuRegistrationLists } from '@/api/stuRegistration'
|
||||||
|
import Popup from '@/components/popup/index.vue'
|
||||||
|
import { usePaging } from '@/hooks/usePaging'
|
||||||
|
import feedback from '@/utils/feedback'
|
||||||
|
|
||||||
|
// 外部传入的已选择班级ID
|
||||||
|
const props = defineProps({
|
||||||
|
selectedIds: {
|
||||||
|
type: Array as PropType<number[]>,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 向父组件传递事件
|
||||||
|
const emit = defineEmits(['success', 'close'])
|
||||||
|
|
||||||
|
// 弹窗相关
|
||||||
|
const popupRef = shallowRef<InstanceType<typeof Popup>>()
|
||||||
|
const popupTitle = ref('选择学生') // 统一弹窗标题
|
||||||
|
|
||||||
|
// 表格引用
|
||||||
|
const tableRef = shallowRef<any>(null)
|
||||||
|
|
||||||
|
// 查询参数
|
||||||
|
const queryParams = reactive({
|
||||||
|
applicationNumber: '',
|
||||||
|
applicationTimeStart: '',
|
||||||
|
applicationTimeEnd: '',
|
||||||
|
isAssigned: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 学院列表
|
||||||
|
const collegeList = ref<any[]>([])
|
||||||
|
|
||||||
|
// 存储与父组件交互的选中班级ID(外部数据)
|
||||||
|
const allSelectedId = ref<number[]>([])
|
||||||
|
// 存储组件内部使用的选中班级ID(内部状态)
|
||||||
|
const allSelectedIds = ref<number[]>([])
|
||||||
|
|
||||||
|
// 分页配置
|
||||||
|
const { pager, getLists: getStudentLists } = usePaging({
|
||||||
|
fetchFun: stuRegistrationLists,
|
||||||
|
params: queryParams
|
||||||
|
})
|
||||||
|
|
||||||
|
// 加载学院列表
|
||||||
|
const fetchColleges = async () => {
|
||||||
|
try {
|
||||||
|
const res = await collegeLists()
|
||||||
|
collegeList.value = res.lists
|
||||||
|
} catch (err) {
|
||||||
|
feedback.msgError('获取学院列表失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理选择变化 - 只修改内部状态
|
||||||
|
const handleSelectionChange = (rows: any[]) => {
|
||||||
|
const currentPageSelectedIds = rows.map((row) => row.id)
|
||||||
|
const currentPageIds = pager.lists.map((row) => row.id)
|
||||||
|
// 移除当前页中取消选中的ID
|
||||||
|
currentPageIds.forEach((id) => {
|
||||||
|
if (!currentPageSelectedIds.includes(id)) {
|
||||||
|
const index = allSelectedIds.value.indexOf(id)
|
||||||
|
if (index > -1) {
|
||||||
|
allSelectedIds.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 添加当前页中新选中的ID
|
||||||
|
currentPageSelectedIds.forEach((id) => {
|
||||||
|
if (!allSelectedIds.value.includes(id)) {
|
||||||
|
allSelectedIds.value.push(id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开弹窗时初始化选中状态
|
||||||
|
const initSelection = async () => {
|
||||||
|
await nextTick()
|
||||||
|
if (!tableRef.value || !pager.lists || pager.lists.length === 0) {
|
||||||
|
console.warn('表格数据未加载完成')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// 先清空所有选中状态
|
||||||
|
tableRef.value.clearSelection()
|
||||||
|
// 遍历当前页数据,设置选中状态(使用外部状态)
|
||||||
|
pager.lists.forEach((row: any) => {
|
||||||
|
if (allSelectedId.value.includes(row.id)) {
|
||||||
|
nextTick(() => {
|
||||||
|
tableRef.value?.toggleRowSelection(row, true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('初始化选中状态失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认选择 - 同步内部状态到外部数据并传递给父组件
|
||||||
|
const handleConfirm = async () => {
|
||||||
|
if (allSelectedIds.value.length === 0) {
|
||||||
|
return feedback.msgWarning('请选择学生')
|
||||||
|
}
|
||||||
|
// 同步内部状态到外部数据
|
||||||
|
allSelectedId.value = [...allSelectedIds.value]
|
||||||
|
emit('success', allSelectedId.value)
|
||||||
|
popupRef.value?.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭弹窗 - 不保存修改,保持原有外部数据
|
||||||
|
const handleClose = () => {
|
||||||
|
// 关闭时恢复内部状态与外部数据一致
|
||||||
|
allSelectedIds.value = [...allSelectedId.value]
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开弹窗方法 - 从父组件传入值同步到内部状态
|
||||||
|
const open = async () => {
|
||||||
|
// 同步父组件传入的值到外部数据
|
||||||
|
allSelectedId.value = [...props.selectedIds]
|
||||||
|
// 同步外部数据到内部状态
|
||||||
|
allSelectedIds.value = [...allSelectedId.value]
|
||||||
|
try {
|
||||||
|
await getStudentLists()
|
||||||
|
popupRef.value?.open()
|
||||||
|
await nextTick()
|
||||||
|
setTimeout(() => {
|
||||||
|
initSelection()
|
||||||
|
}, 100)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('打开弹窗失败:', error)
|
||||||
|
}
|
||||||
|
fetchColleges()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听外部选中ID变化
|
||||||
|
watch(
|
||||||
|
() => props.selectedIds,
|
||||||
|
(val) => {
|
||||||
|
if (!popupRef.value?.visible) {
|
||||||
|
allSelectedId.value = [...val]
|
||||||
|
allSelectedIds.value = [...allSelectedId.value]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// 监听表格数据变化,数据加载完成后自动设置选中状态
|
||||||
|
watch(
|
||||||
|
() => pager.lists,
|
||||||
|
(newLists) => {
|
||||||
|
if (newLists && newLists.length > 0 && popupRef.value?.visible) {
|
||||||
|
setTimeout(() => {
|
||||||
|
initSelection()
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// 监听页码变化
|
||||||
|
watch(
|
||||||
|
() => pager.page,
|
||||||
|
() => {
|
||||||
|
if (popupRef.value?.visible) {
|
||||||
|
allSelectedId.value = [...allSelectedIds.value]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 暴露open方法供父组件调用
|
||||||
|
defineExpose({
|
||||||
|
open
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
@ -61,6 +61,24 @@
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" @click="resetPage">查询</el-button>
|
<el-button type="primary" @click="resetPage">查询</el-button>
|
||||||
<el-button @click="resetParams">重置</el-button>
|
<el-button @click="resetParams">重置</el-button>
|
||||||
|
<el-col v-perms="['info/create.random.info']">
|
||||||
|
<div style="display: flex; flex-wrap: nowrap">
|
||||||
|
<el-input
|
||||||
|
v-model.number="addCount"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
placeholder="数量"
|
||||||
|
class="w-[100px]"
|
||||||
|
/>
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
@click="handleAddRandom(addCount)"
|
||||||
|
class="ml-2"
|
||||||
|
>
|
||||||
|
添加随机学生(测试用)
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
@ -76,6 +94,12 @@
|
||||||
<el-table class="mt-4" size="large" v-loading="pager.loading" :data="pager.lists">
|
<el-table class="mt-4" size="large" v-loading="pager.loading" :data="pager.lists">
|
||||||
<el-table-column label="报名编号" prop="applicationNumber" min-width="100" />
|
<el-table-column label="报名编号" prop="applicationNumber" min-width="100" />
|
||||||
<el-table-column label="邀请码" prop="invitationCode" min-width="100" />
|
<el-table-column label="邀请码" prop="invitationCode" min-width="100" />
|
||||||
|
<el-table-column
|
||||||
|
label="分班状态"
|
||||||
|
prop="isAssigned"
|
||||||
|
min-width="100"
|
||||||
|
:formatter="formatAssignedStatus"
|
||||||
|
/>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="录取状态"
|
label="录取状态"
|
||||||
prop="admissionStatus"
|
prop="admissionStatus"
|
||||||
|
|
@ -138,17 +162,19 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup name="stuRegistration">
|
<script lang="ts" setup name="stuRegistration">
|
||||||
import { stuRegistrationDelete, stuRegistrationLists } from '@/api/stuRegistration'
|
import { stuGenreginfo, stuRegistrationDelete, stuRegistrationLists } from '@/api/stuRegistration'
|
||||||
import { usePaging } from '@/hooks/usePaging'
|
import { usePaging } from '@/hooks/usePaging'
|
||||||
import feedback from '@/utils/feedback'
|
import feedback from '@/utils/feedback'
|
||||||
|
|
||||||
import EditPopup from './edit.vue'
|
import EditPopup from './edit.vue'
|
||||||
const editRef = shallowRef<InstanceType<typeof EditPopup>>()
|
const editRef = shallowRef<InstanceType<typeof EditPopup>>()
|
||||||
const showEdit = ref(false)
|
const showEdit = ref(false)
|
||||||
|
const addCount = ref(1)
|
||||||
const queryParams = reactive({
|
const queryParams = reactive({
|
||||||
baseInfoId: '',
|
baseInfoId: '',
|
||||||
applicationNumber: '',
|
applicationNumber: '',
|
||||||
invitationCode: '',
|
invitationCode: '',
|
||||||
|
isAssigned: '',
|
||||||
admissionStatus: '',
|
admissionStatus: '',
|
||||||
paymentStatus: '',
|
paymentStatus: '',
|
||||||
registrationStatus: '',
|
registrationStatus: '',
|
||||||
|
|
@ -164,6 +190,14 @@ const queryParams = reactive({
|
||||||
registrationTimeEnd: ''
|
registrationTimeEnd: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const formatAssignedStatus = (row: any, column: any, cellValue: any) => {
|
||||||
|
const statusMap: Record<string | number, string> = {
|
||||||
|
'0': '未分班',
|
||||||
|
'1': '已分班'
|
||||||
|
}
|
||||||
|
return statusMap[String(cellValue)] || cellValue
|
||||||
|
}
|
||||||
|
|
||||||
const formatAdmissionStatus = (row: any, column: any, cellValue: any) => {
|
const formatAdmissionStatus = (row: any, column: any, cellValue: any) => {
|
||||||
const statusMap: Record<string | number, string> = {
|
const statusMap: Record<string | number, string> = {
|
||||||
'0': '待审核',
|
'0': '待审核',
|
||||||
|
|
@ -203,6 +237,10 @@ const { pager, getLists, resetPage, resetParams } = usePaging({
|
||||||
params: queryParams
|
params: queryParams
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const handleAddRandom = async (num: any) => {
|
||||||
|
await stuGenreginfo(num)
|
||||||
|
}
|
||||||
|
|
||||||
const handleAdd = async () => {
|
const handleAdd = async () => {
|
||||||
showEdit.value = true
|
showEdit.value = true
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue