Files
school-developer/src/views/basic/basicclass/index.vue
2026-01-14 01:06:25 +08:00

454 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 搜索表单 -->
<el-row v-show="showSearch">
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
<el-form-item label="学院" prop="deptCode">
<el-select
v-model="searchForm.deptCode"
placeholder="请选择学院"
clearable
filterable
style="width: 200px"
@change="handleDeptChange">
<el-option
v-for="item in deptList"
:key="item.deptCode"
:label="item.deptName"
:value="item.deptCode">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="班号" prop="classNo">
<el-input
v-model="searchForm.classNo"
placeholder="请输入班号"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="联院" prop="isUnion">
<el-select
v-model="searchForm.isUnion"
placeholder="请选择联院"
clearable
style="width: 200px">
<el-option label="是" value="1" />
<el-option label="否" value="0" />
</el-select>
</el-form-item>
<el-form-item label="流失率" prop="stuLoseRate">
<el-input
v-model="searchForm.stuLoseRate"
placeholder="请输入流失率"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="入学年份" prop="grade">
<el-input
v-model="searchForm.grade"
placeholder="请输入入学年份"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="班主任" prop="teacherRealName">
<el-input
v-model="searchForm.teacherRealName"
placeholder="请输入班主任姓名"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="班级状态" prop="classStatus">
<el-select
v-model="searchForm.classStatus"
placeholder="请选择班级状态"
clearable
style="width: 200px">
<el-option label="正常" value="0" />
<el-option label="离校" value="1" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" plain icon="Search" @click="handleSearch">查询</el-button>
<el-button icon="Refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-row>
<!-- 操作按钮 -->
<el-row>
<div class="mb8" style="width: 100%">
<el-button
icon="FolderAdd"
type="primary"
class="ml10"
@click="formDialogRef.openDialog()">
</el-button>
<el-button
icon="Link"
type="success"
class="ml10"
@click="handleLinkRule">
关联门禁规则
</el-button>
<el-button
icon="Download"
type="warning"
class="ml10"
@click="handleExport">
</el-button>
<el-button
icon="DocumentAdd"
type="info"
class="ml10"
@click="handleGenerateAssessment">
生成考核班级
</el-button>
<right-toolbar
v-model:showSearch="showSearch"
class="ml10 mr20"
style="float: right;"
@queryTable="getDataList">
</right-toolbar>
</div>
</el-row>
<!-- 表格 -->
<el-table
:data="state.dataList"
v-loading="state.loading"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
@sort-change="sortChangeHandle">
<el-table-column type="index" label="序号" align="center" />
<el-table-column prop="deptName" label="学院" show-overflow-tooltip />
<el-table-column prop="classNo" label="班号" show-overflow-tooltip />
<el-table-column prop="classProName" label="班级规范名称" show-overflow-tooltip />
<el-table-column prop="teacherRealName" label="班主任" show-overflow-tooltip />
<el-table-column prop="teacherTel" label="班主任电话号码" show-overflow-tooltip />
<el-table-column label="班级人数/原始人数" show-overflow-tooltip>
<template #default="scope">
{{ scope.row.stuNum || 0 }}/{{ scope.row.preStuNum || 0 }}
</template>
</el-table-column>
<el-table-column prop="ruleName" label="门禁规则" show-overflow-tooltip />
<el-table-column prop="classStatus" label="班级状态" show-overflow-tooltip>
<template #default="scope">
<span>{{ scope.row.classStatus === '0' ? '正常' : scope.row.classStatus === '1' ? '离校' : '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="stuLoseRate" label="流失率" show-overflow-tooltip>
<template #default="scope">
<span>{{ scope.row.stuLoseRate ? `${scope.row.stuLoseRate}%` : '-' }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" width="200">
<template #default="scope">
<el-button
icon="View"
text
type="primary"
@click="handleViewDetail(scope.row)">
班级概况
</el-button>
<el-button
icon="Edit"
text
type="primary"
@click="formDialogRef.openDialog(scope.row.id)">
编辑
</el-button>
<el-button
icon="Delete"
text
type="danger"
@click="handleDelete([scope.row.id])">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
v-bind="state.pagination" />
</div>
<!-- 编辑新增 -->
<FormDialog ref="formDialogRef" @refresh="getDataList(false)" />
<!-- 班级概况详情 -->
<DetailDialog ref="detailDialogRef" />
<!-- 关联门禁规则对话框 -->
<el-dialog
title="关联门禁规则"
v-model="linkRuleDialogVisible"
:close-on-click-modal="false"
draggable
width="600px">
<el-form :model="linkRuleForm" label-width="120px">
<el-form-item label="选择班级">
<el-select
v-model="linkRuleForm.classCodes"
placeholder="请选择班级"
clearable
filterable
multiple
style="width: 100%">
<el-option
v-for="item in classList"
:key="item.classCode"
:label="item.classNo"
:value="item.classCode">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="选择规则">
<el-select
v-model="linkRuleForm.ruleId"
placeholder="请选择门禁规则"
clearable
filterable
style="width: 100%">
<el-option
v-for="item in ruleList"
:key="item.id"
:label="item.ruleName"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="linkRuleDialogVisible = false"> </el-button>
<el-button type="primary" @click="confirmLinkRule" :loading="linkRuleLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="BasicClass">
import { ref, reactive, defineAsyncComponent, computed, onMounted } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList, delObj, putObjs, classExportData, getDeptList, getClassListByRole } from "/@/api/basic/basicclass";
import { useMessage, useMessageBox } from "/@/hooks/message";
import { fetchList as getRuleList } from "/@/api/stuwork/entrancerule";
import { downBlobFile, adaptationUrl } from "/@/utils/other";
// 引入组件
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
const DetailDialog = defineAsyncComponent(() => import('./detail.vue'));
// 定义变量内容
const formDialogRef = ref()
const detailDialogRef = ref()
const searchFormRef = ref()
// 搜索变量
const showSearch = ref(true)
const deptList = ref<any[]>([])
const classList = ref<any[]>([])
const ruleList = ref<any[]>([])
const linkRuleDialogVisible = ref(false)
const linkRuleLoading = ref(false)
// 搜索表单
const searchForm = reactive({
deptCode: '',
classNo: '',
isUnion: '',
stuLoseRate: '',
grade: '',
teacherRealName: '',
classStatus: ''
})
// 关联门禁规则表单
const linkRuleForm = reactive({
classCodes: [] as string[],
ruleId: ''
})
// 配置 useTable
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: searchForm,
pageList: fetchList,
props: {
item: 'records',
totalCount: 'total'
},
createdIsNeed: true
})
// table hook
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
sortChangeHandle,
tableStyle
} = useTable(state)
// 学院选择变化
const handleDeptChange = () => {
// 可以根据需要清空班号选择
}
// 查询
const handleSearch = () => {
getDataList()
}
// 重置
const handleReset = () => {
searchFormRef.value?.resetFields()
searchForm.deptCode = ''
searchForm.classNo = ''
searchForm.isUnion = ''
searchForm.stuLoseRate = ''
searchForm.grade = ''
searchForm.teacherRealName = ''
searchForm.classStatus = ''
getDataList()
}
// 删除
const handleDelete = async (ids: string[]) => {
try {
await useMessageBox().confirm('确定要删除选中的记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
} catch {
return
}
try {
// 根据API文件delObj接受单个id需要循环删除
for (const id of ids) {
await delObj(id)
}
useMessage().success('删除成功')
getDataList()
} catch (err: any) {
useMessage().error(err.msg || '删除失败')
}
}
// 查看班级概况 - 直接使用表格行数据
const handleViewDetail = (row: any) => {
if (detailDialogRef.value) {
detailDialogRef.value.openDialog(row)
}
}
// 关联门禁规则
const handleLinkRule = () => {
linkRuleDialogVisible.value = true
linkRuleForm.classCodes = []
linkRuleForm.ruleId = ''
// 加载规则列表
getRuleListData()
}
// 确认关联门禁规则
const confirmLinkRule = async () => {
if (!linkRuleForm.ruleId) {
useMessage().warning('请选择门禁规则')
return
}
if (!linkRuleForm.classCodes || linkRuleForm.classCodes.length === 0) {
useMessage().warning('请选择班级')
return
}
try {
linkRuleLoading.value = true
await putObjs({
ruleId: linkRuleForm.ruleId,
classCodes: linkRuleForm.classCodes
})
useMessage().success('关联成功')
linkRuleDialogVisible.value = false
getDataList()
} catch (err: any) {
useMessage().error(err.msg || '关联失败')
} finally {
linkRuleLoading.value = false
}
}
// 导出
const handleExport = async () => {
try {
await downBlobFile(adaptationUrl('/basic/basicclass/classExportData'), searchForm, '班级信息.xlsx')
useMessage().success('导出成功')
} catch (err: any) {
useMessage().error(err.msg || '导出失败')
}
}
// 生成考核班级
const handleGenerateAssessment = () => {
// TODO: 需要确认接口地址
useMessage().warning('生成考核班级功能待实现,请确认接口地址')
}
// 获取学院列表
const getDeptListData = async () => {
try {
const res = await getDeptList()
if (res.data) {
deptList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取学院列表失败', err)
deptList.value = []
}
}
// 获取班号列表
const getClassListData = async () => {
try {
const res = await getClassListByRole()
if (res.data) {
classList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取班号列表失败', err)
classList.value = []
}
}
// 获取门禁规则列表
const getRuleListData = async () => {
try {
const res = await getRuleList({ current: 1, size: 9999 })
if (res.data && res.data.records) {
ruleList.value = Array.isArray(res.data.records) ? res.data.records : []
} else if (Array.isArray(res.data)) {
ruleList.value = res.data
} else {
ruleList.value = []
}
} catch (err) {
console.error('获取门禁规则列表失败', err)
ruleList.value = []
}
}
// 初始化
onMounted(() => {
getDeptListData()
getClassListData()
})
</script>