@ -1,10 +1,5 @@
< template >
< div class = "school-admin-workbench" >
< section class = "admin-hero" >
< div class = "admin-badge" > School Admin · Student < / div >
< h1 class = "admin-title" > 学生管理 < / h1 >
< p class = "admin-subtitle" > 管理本校学生账号 , 学生数据按班级归档并限制在当前学校 。 < / p >
< / section >
< section class = "admin-panel" >
< div class = "toolbar" >
< el -input v -model = " query.name " clearable placeholder = "学生姓名" class = "filter-input" @keyup.enter ="load" / >
@ -14,8 +9,8 @@
< / e l - s e l e c t >
< el -button type = "primary" @click ="load" > 查 询 < / el -button >
< el -button @click ="reset" > 重 置 < / el -button >
< el -button type = " success " @click ="openCreate" > 新 增 < / el -button >
< el -button type = "warning" @click ="downloadStudentTemplate" > 下 载 模 板 < / el -button >
< el -button type = " primary " @click ="openCreate" > 新 增 < / el -button >
< el -button @click ="downloadStudentTemplate" > 下 载 模 板 < / el -button >
< el -button type = "primary" :loading ="importing" @click ="triggerImport" > Excel 导 入 < / el -button >
< input ref = "importInputRef" type = "file" accept = ".xls,.xlsx" class = "excel-import-input" @change ="handleImportFile" / >
< / div >
@ -40,19 +35,9 @@
< el -form :model ="form" label -width = " 86px " >
< el -form -item label = "姓名" > < el -input v -model = " form.name " / > < / e l - f o r m - i t e m >
< el -form -item label = "学号" > < el -input v -model = " form.username " :disabled ="Boolean(form.userId)" / > < / e l - f o r m - i t e m >
< el -form -item label = "所属院系" >
< el -select v-model ="form.schoolFacultyId" placeholder="请选择院系" style="width: 100%" @change="handleFacultyChange" >
< el -option v -for = " faculty in faculties " :key ="faculty.schoolFacultyId" :label ="faculty.schoolFacultyName" :value ="faculty.schoolFacultyId" / >
< / e l - s e l e c t >
< / e l - f o r m - i t e m >
< el -form -item label = "所属专业" >
< el -select v-model ="form.schoolMajorId" placeholder="请选择专业" style="width: 100%" :disabled="!form.schoolFacultyId" @change="handleMajorChange" >
< el -option v -for = " major in formMajors " :key ="major.schoolMajorId" :label ="major.schoolMajorName" :value ="major.schoolMajorId" / >
< / e l - s e l e c t >
< / e l - f o r m - i t e m >
< el -form -item label = "班级" >
< el -select v-model ="form.schoolClassId" placeholder="请选择班级" style="width: 100%" :disabled="!form.schoolMajorId" >
< el -option v -for = " klass in formC lasses" :key ="klass.schoolClassId" :label ="klass.className" :value ="klass.schoolClassId" / >
< el -select v-model ="form.schoolClassId" placeholder="请选择班级" style="width: 100%" >
< el -option v -for = " klass in classes " :key ="klass.schoolClassId" :label ="klass.className" :value ="klass.schoolClassId" / >
< / e l - s e l e c t >
< / e l - f o r m - i t e m >
< el -form -item label = "性别" >
@ -73,7 +58,7 @@
< script setup >
import "@/views/schoolAdmin/admin-theme.scss" ;
import { ElMessage , ElMessageBox } from "element-plus" ;
import { addAdminStudent , deleteAdminStudent , getAdminClasses , getAdmin Faculties, getAdminMajors , getAdmin Students, importAdminStudents , updateAdminStudent } from "@/api/schoolAdmin" ;
import { addAdminStudent , deleteAdminStudent , getAdminClasses , getAdmin Students, importAdminStudents , updateAdminStudent } from "@/api/schoolAdmin" ;
const query = reactive ( { index : 1 , size : 10 , name : "" , username : "" , schoolClassId : "" } ) ;
const rows = ref ( [ ] ) ;
@ -83,19 +68,13 @@ const importing = ref(false);
const dialogVisible = ref ( false ) ;
const importInputRef = ref ( null ) ;
const form = ref ( { } ) ;
const faculties = ref ( [ ] ) ;
const majors = ref ( [ ] ) ;
const classes = ref ( [ ] ) ;
const studentTemplateHeaders = [ "姓名" , "学号" , "性别" , "所属 院系", "所属专业" , "所属 班级"] ;
const studentTemplateHeaders = [ "姓名" , "学号" , "性别" , "所属班级" ] ;
const sexOptions = [
{ label : "男" , value : "男" } ,
{ label : "女" , value : "女" } ,
] ;
const className = ( id ) => classes . value . find ( ( klass ) => klass . schoolClassId === id ) ? . className || "" ;
const classMajorId = ( id ) => classes . value . find ( ( klass ) => klass . schoolClassId === id ) ? . schoolMajorId || "" ;
const majorFacultyId = ( id ) => majors . value . find ( ( major ) => major . schoolMajorId === id ) ? . schoolFacultyId || "" ;
const formMajors = computed ( ( ) => majors . value . filter ( ( major ) => major . schoolFacultyId === form . value . schoolFacultyId ) ) ;
const formClasses = computed ( ( ) => classes . value . filter ( ( klass ) => klass . schoolMajorId === form . value . schoolMajorId ) ) ;
const trimValue = ( value ) => String ( value ? ? "" ) . trim ( ) ;
const normalizeSex = ( sex ) => {
const value = trimValue ( sex ) ;
@ -104,64 +83,37 @@ const normalizeSex = (sex) => {
return "" ;
} ;
const loadFaculties = async ( ) => {
const res = await getAdminFaculties ( { index : 1 , size : 1000 } ) ;
faculties . value = ( res . data ? . list || [ ] ) . map ( ( row ) => row . schoolFaculty || row ) ;
} ;
const loadMajors = async ( ) => {
const res = await getAdminMajors ( { index : 1 , size : 1000 } ) ;
majors . value = ( res . data ? . list || [ ] ) . map ( ( row ) => ( {
... ( row . schoolMajor || row ) ,
schoolFacultyId : row . schoolFacultyId || row . schoolMajor ? . schoolFacultyId ,
schoolMajorId : row . schoolMajorId || row . schoolMajor ? . schoolMajorId ,
} ) ) ;
} ;
const loadClasses = async ( ) => {
const res = await getAdminClasses ( { index : 1 , size : 1000 } ) ;
classes . value = ( res . data ? . list || [ ] ) . map ( ( row ) => row . schoolClass || row ) ;
} ;
const ensureOrgData = async ( ) => {
await Promise . all ( [ loadFaculties ( ) , loadMajors ( ) , loadClasses ( ) ] ) ;
} ;
const orgRows = ( ) =>
classes . value . map ( ( klass ) => {
const major = majors . value . find ( ( item ) => item . schoolMajorId === klass . schoolMajorId ) || { } ;
const faculty = faculties . value . find ( ( item ) => item . schoolFacultyId === major . schoolFacultyId ) || { } ;
return {
所属院系 : faculty . schoolFacultyName || "" ,
所属专业 : major . schoolMajorName || "" ,
所属班级 : klass . className || "" ,
} ;
} ) ;
const firstOrgRow = ( ) => orgRows ( ) . find ( ( row ) => row . 所属院系 && row . 所属专业 && row . 所属班级 ) || { 所属院系 : "示例院系" , 所属专业 : "示例专业" , 所属班级 : "示例班级" } ;
const downloadStudentTemplate = async ( ) => {
const XLSX = await import ( "xlsx" ) ;
await ensureOrgData ( ) ;
const sampleOrg = firstOrgRow ( ) ;
const template Rows = [
await loadClasses ( ) ;
const classNames = classes . value . map ( ( klass ) => klass . className || "" ) . filter ( Boolean ) ;
const templateSheet = XLSX . utils . aoa _to _sheet ( [
studentTemplateHeaders ,
[ "张三" , "20260001" , "男" , sampleOrg . 所属院系 , sampleOrg . 所属专业 , sampleOrg . 所属班级 ] ,
] ;
const templateSheet = XLSX . utils . aoa _to _sheet ( templateRows ) ;
templateSheet [ "!cols" ] = [ { wch : 14 } , { wch : 18 } , { wch : 10 } , { wch : 18 } , { wch : 18 } , { wch : 18 } ] ;
templateSheet [ "!autofilter" ] = { ref : "A1:F1" } ;
[ "张三" , "20260001" , "男" , classNames [ 0 ] || "行政一班" ] ,
] ) ;
templateSheet [ "!cols" ] = [ { wch : 14 } , { wch : 18 } , { wch : 10 } , { wch : 20 } ] ;
templateSheet [ "!autofilter" ] = { ref : "A1:D1" } ;
const orgSheet = XLSX . utils . json _to _sheet ( orgRows ( ) . length ? orgRows ( ) : [ { 所属院系 : "请先维护院系" , 所属专业 : "请先维护专业" , 所属班级 : "请先维护班级" } ] ) ;
org Sheet[ "!cols" ] = [ { wch : 22 } , { wch : 22 } , { wch : 22 } ] ;
const classSheet = XLSX . utils . aoa _to _sheet ( [ [ "所属班级" ] , ... classNames . map ( ( name ) => [ name ] ) ] ) ;
classSheet [ "!cols" ] = [ { wch : 22 } ] ;
const guideSheet = XLSX . utils . aoa _to _sheet ( [
[ "填写说明" ] ,
[ "1. 请在“学生导入模板”中填写学生信息,第一行表头不要修改。" ] ,
[ "2. 必填列:姓名、学号、所属 院系、所属专业、所属 班级。"] ,
[ "3. 性别请填写“男”或“女”,也兼容 1=男、2=女 。"] ,
[ "4. 所属 院系、所属专业、所属 班级请填写名称,不要填写数据 ID。"] ,
[ "5. 可在“基础数据”表中查看当前学校可用的 院系/专业/班级组合 。"] ,
[ "2. 必填列:姓名、学号、所属 班级。"] ,
[ "3. 性别请填写“男”或“女”,也兼容 1=男、2=女 ,可留空 。"] ,
[ "4. 所属 班级请填写名称,不要填写数据 ID;只能导入本校行政班 。"] ,
[ "5. 可在“基础数据”表中查看当前学校可用的 班级名称 。"] ,
] ) ;
guideSheet [ "!cols" ] = [ { wch : 80 } ] ;
const workbook = XLSX . utils . book _new ( ) ;
XLSX . utils . book _append _sheet ( workbook , templateSheet , "学生导入模板" ) ;
XLSX . utils . book _append _sheet ( workbook , org Sheet, "基础数据" ) ;
XLSX . utils . book _append _sheet ( workbook , class Sheet, "基础数据" ) ;
XLSX . utils . book _append _sheet ( workbook , guideSheet , "填写说明" ) ;
XLSX . writeFile ( workbook , "学生导入模板.xlsx" ) ;
} ;
@ -212,26 +164,16 @@ const reset = () => {
load ( ) ;
} ;
const openCreate = ( ) => {
form . value = { school FacultyId: "" , schoolMajorId : "" , school ClassId: "" , sex : "" } ;
form . value = { school ClassId: "" , sex : "" } ;
dialogVisible . value = true ;
} ;
const openEdit = ( row ) => {
const schoolMajorId = row . schoolMajorId || classMajorId ( row . schoolClassId ) ;
form . value = {
... row ,
sex : normalizeSex ( row . sex ) ,
schoolMajorId ,
schoolFacultyId : row . schoolFacultyId || majorFacultyId ( schoolMajorId ) ,
} ;
dialogVisible . value = true ;
} ;
const handleFacultyChange = ( ) => {
form . value . schoolMajorId = "" ;
form . value . schoolClassId = "" ;
} ;
const handleMajorChange = ( ) => {
form . value . schoolClassId = "" ;
} ;
const submit = async ( ) => {
if ( form . value . userId ) {
await updateAdminStudent ( form . value . userId , form . value ) ;
@ -250,8 +192,6 @@ const remove = async (row) => {
} ;
onMounted ( ( ) => {
loadFaculties ( ) ;
loadMajors ( ) ;
loadClasses ( ) ;
load ( ) ;
} ) ;