|
|
|
@ -2,7 +2,7 @@
|
|
|
|
<div class="page-container fade-in-up">
|
|
|
|
<div class="page-container fade-in-up">
|
|
|
|
<div class="page-header">
|
|
|
|
<div class="page-header">
|
|
|
|
<h2 class="page-title">学生信息</h2>
|
|
|
|
<h2 class="page-title">学生信息</h2>
|
|
|
|
<p class="page-subtitle">管理学生信息,支持批量导入与编辑</p>
|
|
|
|
<p class="page-subtitle">管理学生信息,支持新增与编辑</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="filter-bar">
|
|
|
|
<div class="filter-bar">
|
|
|
|
@ -20,6 +20,13 @@
|
|
|
|
<div class="data-table-card">
|
|
|
|
<div class="data-table-card">
|
|
|
|
<el-table :data="students" stripe v-loading="loading" @selection-change="handleSelectionChange">
|
|
|
|
<el-table :data="students" stripe v-loading="loading" @selection-change="handleSelectionChange">
|
|
|
|
<el-table-column type="selection" width="45" />
|
|
|
|
<el-table-column type="selection" width="45" />
|
|
|
|
|
|
|
|
<el-table-column label="头像" width="80" align="center">
|
|
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
|
|
<el-avatar :src="row.faceImage ? fileHttp + '/' + row.faceImage : ''" :size="40">
|
|
|
|
|
|
|
|
{{ row.name?.charAt(0) }}
|
|
|
|
|
|
|
|
</el-avatar>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
</el-table-column>
|
|
|
|
<el-table-column prop="studentNo" label="学号" width="120" />
|
|
|
|
<el-table-column prop="studentNo" label="学号" width="120" />
|
|
|
|
<el-table-column prop="name" label="姓名" width="100" />
|
|
|
|
<el-table-column prop="name" label="姓名" width="100" />
|
|
|
|
<el-table-column prop="gender" label="性别" width="70" align="center">
|
|
|
|
<el-table-column prop="gender" label="性别" width="70" align="center">
|
|
|
|
@ -56,12 +63,18 @@
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 添加/编辑弹窗 -->
|
|
|
|
<!-- 添加/编辑弹窗 -->
|
|
|
|
<el-dialog v-model="dialogVisible" :title="editing ? '编辑学生信息' : '添加学生'" width="520px" destroy-on-close>
|
|
|
|
<el-dialog v-model="dialogVisible" :title="editing ? '编辑学生信息' : '添加学生'" width="640px" destroy-on-close top="3vh">
|
|
|
|
<el-form ref="formRef" :model="form" :rules="formRules" label-width="80px" label-position="left">
|
|
|
|
<div class="dialog-scroll-body">
|
|
|
|
<el-form-item label="学号" required>
|
|
|
|
<div v-if="editing" class="edit-avatar">
|
|
|
|
|
|
|
|
<el-avatar :src="form.faceImage ? fileHttp + '/' + form.faceImage : ''" :size="72">
|
|
|
|
|
|
|
|
{{ form.name?.charAt(0) }}
|
|
|
|
|
|
|
|
</el-avatar>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<el-form ref="formRef" :model="form" :rules="formRules" label-width="90px" label-position="left">
|
|
|
|
|
|
|
|
<el-form-item label="学号" prop="studentNo">
|
|
|
|
<el-input v-model="form.studentNo" placeholder="请输入学号" />
|
|
|
|
<el-input v-model="form.studentNo" placeholder="请输入学号" />
|
|
|
|
</el-form-item>
|
|
|
|
</el-form-item>
|
|
|
|
<el-form-item label="姓名" required>
|
|
|
|
<el-form-item label="姓名" prop="name">
|
|
|
|
<el-input v-model="form.name" placeholder="请输入姓名" />
|
|
|
|
<el-input v-model="form.name" placeholder="请输入姓名" />
|
|
|
|
</el-form-item>
|
|
|
|
</el-form-item>
|
|
|
|
<el-form-item label="性别">
|
|
|
|
<el-form-item label="性别">
|
|
|
|
@ -70,7 +83,7 @@
|
|
|
|
<el-radio :value="0">女</el-radio>
|
|
|
|
<el-radio :value="0">女</el-radio>
|
|
|
|
</el-radio-group>
|
|
|
|
</el-radio-group>
|
|
|
|
</el-form-item>
|
|
|
|
</el-form-item>
|
|
|
|
<el-form-item label="班级" required>
|
|
|
|
<el-form-item label="班级" prop="classId">
|
|
|
|
<el-select v-model="form.classId" placeholder="请选择班级" style="width: 100%">
|
|
|
|
<el-select v-model="form.classId" placeholder="请选择班级" style="width: 100%">
|
|
|
|
<el-option v-for="c in classList" :key="c.id" :label="c.className" :value="c.id" />
|
|
|
|
<el-option v-for="c in classList" :key="c.id" :label="c.className" :value="c.id" />
|
|
|
|
</el-select>
|
|
|
|
</el-select>
|
|
|
|
@ -84,18 +97,72 @@
|
|
|
|
<el-radio :value="0">离校</el-radio>
|
|
|
|
<el-radio :value="0">离校</el-radio>
|
|
|
|
</el-radio-group>
|
|
|
|
</el-radio-group>
|
|
|
|
</el-form-item>
|
|
|
|
</el-form-item>
|
|
|
|
</el-form>
|
|
|
|
<el-form-item v-if="!editing" label="正脸照片" required>
|
|
|
|
|
|
|
|
<el-upload
|
|
|
|
|
|
|
|
v-model:file-list="frontImages"
|
|
|
|
|
|
|
|
list-type="picture-card"
|
|
|
|
|
|
|
|
:auto-upload="false"
|
|
|
|
|
|
|
|
accept="image/*"
|
|
|
|
|
|
|
|
multiple
|
|
|
|
|
|
|
|
:on-preview="handlePreview"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<el-icon><Plus /></el-icon>
|
|
|
|
|
|
|
|
</el-upload>
|
|
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
<el-form-item v-if="!editing" label="左脸照片" required>
|
|
|
|
|
|
|
|
<el-upload
|
|
|
|
|
|
|
|
v-model:file-list="leftImages"
|
|
|
|
|
|
|
|
list-type="picture-card"
|
|
|
|
|
|
|
|
:auto-upload="false"
|
|
|
|
|
|
|
|
accept="image/*"
|
|
|
|
|
|
|
|
multiple
|
|
|
|
|
|
|
|
:on-preview="handlePreview"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<el-icon><Plus /></el-icon>
|
|
|
|
|
|
|
|
</el-upload>
|
|
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
<el-form-item v-if="!editing" label="右脸照片" required>
|
|
|
|
|
|
|
|
<el-upload
|
|
|
|
|
|
|
|
v-model:file-list="rightImages"
|
|
|
|
|
|
|
|
list-type="picture-card"
|
|
|
|
|
|
|
|
:auto-upload="false"
|
|
|
|
|
|
|
|
accept="image/*"
|
|
|
|
|
|
|
|
multiple
|
|
|
|
|
|
|
|
:on-preview="handlePreview"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<el-icon><Plus /></el-icon>
|
|
|
|
|
|
|
|
</el-upload>
|
|
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
<div v-if="!editing" class="upload-notice">
|
|
|
|
|
|
|
|
<el-alert
|
|
|
|
|
|
|
|
type="warning"
|
|
|
|
|
|
|
|
:closable="false"
|
|
|
|
|
|
|
|
show-icon
|
|
|
|
|
|
|
|
title="请谨慎选择照片,提交后不支持在线修改已上传的图片"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
<template #footer>
|
|
|
|
<template #footer>
|
|
|
|
<el-button @click="dialogVisible = false">取消</el-button>
|
|
|
|
<el-button @click="dialogVisible = false">取消</el-button>
|
|
|
|
<el-button type="primary" :loading="submitting" @click="saveStudent">保存</el-button>
|
|
|
|
<el-button type="primary" :loading="submitting" @click="saveStudent">保存</el-button>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
</el-dialog>
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 图片预览弹窗 -->
|
|
|
|
|
|
|
|
<el-dialog v-model="previewVisible" title="图片预览" width="600px" destroy-on-close>
|
|
|
|
|
|
|
|
<div class="preview-body">
|
|
|
|
|
|
|
|
<el-image :src="previewUrl" fit="contain" style="width: 100%; max-height: 70vh" />
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</el-dialog>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
<script setup>
|
|
|
|
import { ref, computed, watch, onMounted } from 'vue'
|
|
|
|
import { ref, computed, watch, onMounted } from 'vue'
|
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
|
|
|
|
|
import { Search, Plus, Edit, Delete } from '@element-plus/icons-vue'
|
|
|
|
|
|
|
|
import fileHttp from '@/utils/fileHttp'
|
|
|
|
import { getStudentPage, getClassList, addStudent, updateStudent, deleteStudent } from '@/api/info'
|
|
|
|
import { getStudentPage, getClassList, addStudent, updateStudent, deleteStudent } from '@/api/info'
|
|
|
|
|
|
|
|
|
|
|
|
const searchKey = ref('')
|
|
|
|
const searchKey = ref('')
|
|
|
|
@ -111,9 +178,32 @@ const selectedRows = ref([])
|
|
|
|
const hasSelected = ref(false)
|
|
|
|
const hasSelected = ref(false)
|
|
|
|
const classList = ref([])
|
|
|
|
const classList = ref([])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 照片墙
|
|
|
|
|
|
|
|
const frontImages = ref([])
|
|
|
|
|
|
|
|
const leftImages = ref([])
|
|
|
|
|
|
|
|
const rightImages = ref([])
|
|
|
|
|
|
|
|
|
|
|
|
const formRef = ref(null)
|
|
|
|
const formRef = ref(null)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 图片预览
|
|
|
|
|
|
|
|
const previewVisible = ref(false)
|
|
|
|
|
|
|
|
const previewUrl = ref('')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handlePreview = (file) => {
|
|
|
|
|
|
|
|
previewUrl.value = file.url || URL.createObjectURL(file.raw)
|
|
|
|
|
|
|
|
previewVisible.value = true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const formRules = {
|
|
|
|
const formRules = {
|
|
|
|
|
|
|
|
studentNo: [
|
|
|
|
|
|
|
|
{ required: true, message: '请输入学号', trigger: 'blur' }
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
name: [
|
|
|
|
|
|
|
|
{ required: true, message: '请输入姓名', trigger: 'blur' }
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
classId: [
|
|
|
|
|
|
|
|
{ required: true, message: '请选择班级', trigger: 'change' }
|
|
|
|
|
|
|
|
],
|
|
|
|
phone: [
|
|
|
|
phone: [
|
|
|
|
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
|
|
|
|
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
|
|
|
|
]
|
|
|
|
]
|
|
|
|
@ -202,6 +292,9 @@ const handleSelectionChange = (rows) => {
|
|
|
|
const showAddDialog = () => {
|
|
|
|
const showAddDialog = () => {
|
|
|
|
editing.value = false
|
|
|
|
editing.value = false
|
|
|
|
form.value = { id: null, studentNo: '', name: '', gender: 1, classId: '', phone: '', status: 1 }
|
|
|
|
form.value = { id: null, studentNo: '', name: '', gender: 1, classId: '', phone: '', status: 1 }
|
|
|
|
|
|
|
|
frontImages.value = []
|
|
|
|
|
|
|
|
leftImages.value = []
|
|
|
|
|
|
|
|
rightImages.value = []
|
|
|
|
dialogVisible.value = true
|
|
|
|
dialogVisible.value = true
|
|
|
|
formRef.value?.clearValidate()
|
|
|
|
formRef.value?.clearValidate()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -215,8 +308,12 @@ const editStudent = (row) => {
|
|
|
|
gender: row.gender,
|
|
|
|
gender: row.gender,
|
|
|
|
classId: row.classId,
|
|
|
|
classId: row.classId,
|
|
|
|
phone: row.phone || '',
|
|
|
|
phone: row.phone || '',
|
|
|
|
status: row.status
|
|
|
|
status: row.status,
|
|
|
|
|
|
|
|
faceImage: row.faceImage || ''
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
frontImages.value = []
|
|
|
|
|
|
|
|
leftImages.value = []
|
|
|
|
|
|
|
|
rightImages.value = []
|
|
|
|
dialogVisible.value = true
|
|
|
|
dialogVisible.value = true
|
|
|
|
formRef.value?.clearValidate()
|
|
|
|
formRef.value?.clearValidate()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -225,21 +322,37 @@ const saveStudent = async () => {
|
|
|
|
const valid = await formRef.value?.validate().catch(() => false)
|
|
|
|
const valid = await formRef.value?.validate().catch(() => false)
|
|
|
|
if (!valid) return
|
|
|
|
if (!valid) return
|
|
|
|
|
|
|
|
|
|
|
|
submitting.value = true
|
|
|
|
// 新增时至少上传一张图片
|
|
|
|
const payload = {
|
|
|
|
if (!editing.value && frontImages.value.length + leftImages.value.length + rightImages.value.length === 0) {
|
|
|
|
studentNo: form.value.studentNo,
|
|
|
|
ElMessage.warning('请至少上传一张学生照片')
|
|
|
|
name: form.value.name,
|
|
|
|
return
|
|
|
|
gender: form.value.gender,
|
|
|
|
|
|
|
|
classId: form.value.classId,
|
|
|
|
|
|
|
|
phone: form.value.phone,
|
|
|
|
|
|
|
|
status: form.value.status
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
submitting.value = true
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
if (editing.value) {
|
|
|
|
if (editing.value) {
|
|
|
|
payload.id = form.value.id
|
|
|
|
const payload = {
|
|
|
|
|
|
|
|
id: form.value.id,
|
|
|
|
|
|
|
|
studentNo: form.value.studentNo,
|
|
|
|
|
|
|
|
name: form.value.name,
|
|
|
|
|
|
|
|
gender: form.value.gender,
|
|
|
|
|
|
|
|
classId: form.value.classId,
|
|
|
|
|
|
|
|
phone: form.value.phone,
|
|
|
|
|
|
|
|
status: form.value.status
|
|
|
|
|
|
|
|
}
|
|
|
|
await updateStudent(payload)
|
|
|
|
await updateStudent(payload)
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
await addStudent(payload)
|
|
|
|
const fd = new FormData()
|
|
|
|
|
|
|
|
fd.append('studentNo', form.value.studentNo)
|
|
|
|
|
|
|
|
fd.append('name', form.value.name)
|
|
|
|
|
|
|
|
fd.append('gender', form.value.gender)
|
|
|
|
|
|
|
|
fd.append('classId', form.value.classId || '')
|
|
|
|
|
|
|
|
fd.append('phone', form.value.phone || '')
|
|
|
|
|
|
|
|
fd.append('status', form.value.status)
|
|
|
|
|
|
|
|
frontImages.value.forEach(f => fd.append('frontImage', f.raw))
|
|
|
|
|
|
|
|
leftImages.value.forEach(f => fd.append('leftImage', f.raw))
|
|
|
|
|
|
|
|
rightImages.value.forEach(f => fd.append('rightImage', f.raw))
|
|
|
|
|
|
|
|
await addStudent(fd)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ElMessage.success(editing.value ? '编辑成功' : '添加成功')
|
|
|
|
ElMessage.success(editing.value ? '编辑成功' : '添加成功')
|
|
|
|
dialogVisible.value = false
|
|
|
|
dialogVisible.value = false
|
|
|
|
@ -290,5 +403,41 @@ const batchDelete = () => {
|
|
|
|
border-radius: 8px;
|
|
|
|
border-radius: 8px;
|
|
|
|
padding: 20px;
|
|
|
|
padding: 20px;
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-avatar) {
|
|
|
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.edit-avatar {
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-avatar) {
|
|
|
|
|
|
|
|
font-size: 30px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.dialog-scroll-body {
|
|
|
|
|
|
|
|
max-height: calc(85vh - 160px);
|
|
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
|
|
padding-right: 4px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-upload--picture-card) {
|
|
|
|
|
|
|
|
width: 90px;
|
|
|
|
|
|
|
|
height: 90px;
|
|
|
|
|
|
|
|
line-height: 90px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-upload-list--picture-card .el-upload-list__item) {
|
|
|
|
|
|
|
|
width: 90px;
|
|
|
|
|
|
|
|
height: 90px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.upload-notice {
|
|
|
|
|
|
|
|
margin-top: -8px;
|
|
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
</style>
|
|
|
|
|