feat:学生信息接口联调

master
zhoulexin 2 weeks ago
parent fd149325e9
commit de90d5284a

@ -79,6 +79,11 @@ export function deleteCamera(ids) {
// ==================== 班级信息 ====================
// 获取班级列表(下拉用,返回全部)
export function getClassList() {
return request({ url: '/class/list', method: 'get' })
}
// 获取班级列表(分页)
export function getClasses(params) {
return request({ url: '/class/page', method: 'get', params })
@ -99,6 +104,28 @@ export function deleteClass(ids) {
return request({ url: '/class', method: 'delete', data: ids })
}
// ==================== 学生信息 ====================
// 获取学生列表(分页 + 关键字搜索)
export function getStudentPage(params) {
return request({ url: '/student/page', method: 'get', params })
}
// 新增学生
export function addStudent(data) {
return request({ url: '/student', method: 'post', data })
}
// 编辑学生
export function updateStudent(data) {
return request({ url: `/student/${data.id}`, method: 'put', data })
}
// 删除学生(支持批量,传入 id 数组)
export function deleteStudent(ids) {
return request({ url: '/student', method: 'delete', data: ids })
}
// ==================== 教师信息 ====================
// 获取教师列表(下拉用,返回全部)

@ -16,6 +16,7 @@
:default-active="activeMenu"
:collapse="appStore.sidebarCollapsed"
:collapse-transition="false"
:default-openeds="['/info']"
router
background-color="transparent"
text-color="#525252"
@ -41,30 +42,11 @@
<span>数据展示大屏</span>
</el-menu-item>
<el-sub-menu index="/settings">
<template #title>
<el-icon><Setting /></el-icon>
<span>系统设置</span>
</template>
<el-menu-item index="/settings/device">
<el-icon><Monitor /></el-icon>
<span>设备管理</span>
</el-menu-item>
<el-menu-item index="/settings/rules">
<el-icon><Notebook /></el-icon>
<span>考勤规则设置</span>
</el-menu-item>
<el-menu-item index="/settings/permissions">
<el-icon><Lock /></el-icon>
<span>权限管理</span>
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="/info">
<template #title>
<el-icon><List /></el-icon>
<span>信息管理</span>
</template>
</template>
<el-menu-item index="/info/building">
<el-icon><OfficeBuilding /></el-icon>
<span>教学楼信息</span>
@ -86,6 +68,25 @@
<span>课程信息</span>
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="/settings">
<template #title>
<el-icon><Setting /></el-icon>
<span>系统设置</span>
</template>
<el-menu-item index="/settings/device">
<el-icon><Monitor /></el-icon>
<span>设备管理</span>
</el-menu-item>
<el-menu-item index="/settings/rules">
<el-icon><Notebook /></el-icon>
<span>考勤规则设置</span>
</el-menu-item>
<el-menu-item index="/settings/permissions">
<el-icon><Lock /></el-icon>
<span>权限管理</span>
</el-menu-item>
</el-sub-menu>
</el-menu>
</el-scrollbar>

@ -8,44 +8,46 @@
<div class="filter-bar">
<el-input v-model="searchKey" placeholder="搜索姓名/学号..." :prefix-icon="Search" clearable size="default" style="width: 220px" />
<el-select v-model="classFilter" placeholder="班级筛选" clearable size="default" style="width: 180px">
<el-option label="计算机2021-1班" value="cs1" />
<el-option label="软件工程2022-2班" value="se2" />
<el-option label="人工智能2023-1班" value="ai1" />
<el-option v-for="c in classList" :key="c.id" :label="c.className" :value="c.id" />
</el-select>
<el-upload action="#" :show-file-list="false" accept=".xlsx,.xls">
<!-- <el-upload action="#" :show-file-list="false" accept=".xlsx,.xls">
<el-button :icon="Upload">导入Excel</el-button>
</el-upload>
</el-upload> -->
<el-button type="primary" :icon="Plus" @click="showAddDialog"></el-button>
<el-button :icon="Delete" :disabled="!hasSelected" @click="batchDelete"></el-button>
</div>
<div class="data-table-card">
<el-table :data="students" stripe @selection-change="handleSelectionChange">
<el-table :data="students" stripe v-loading="loading" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="45" />
<el-table-column prop="id" label="学号" width="120" />
<el-table-column prop="studentNo" label="学号" width="120" />
<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">
<template #default="{ row }">
{{ row.gender === 1 ? '男' : '女' }}
</template>
</el-table-column>
<el-table-column prop="className" label="班级" min-width="160" />
<el-table-column prop="phone" label="手机号" width="140" />
<el-table-column prop="status" label="状态" width="80" align="center">
<template #default="{ row }">
<el-tag :type="row.status === '正常' ? 'success' : 'info'" size="small">
{{ row.status }}
<el-tag :type="row.status === 1 ? 'success' : 'info'" size="small">
{{ row.status === 1 ? '在读' : '离校' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="160" align="center" fixed="right">
<template #default="{ row }">
<el-button link type="primary" size="small" :icon="Edit" @click="editStudent(row)"></el-button>
<el-button link type="danger" size="small" :icon="Delete" @click="deleteStudent(row)"></el-button>
<el-button link type="danger" size="small" :icon="Delete" @click="deleteOne(row)"></el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
v-model:current-page="pageCurrent"
:total="students.length"
:page-size="10"
:total="total"
:page-size="pageSize"
size="small"
background
layout="total, prev, pager, next"
@ -55,64 +57,142 @@
<!-- 添加/编辑弹窗 -->
<el-dialog v-model="dialogVisible" :title="editing ? '编辑学生信息' : '添加学生'" width="520px" destroy-on-close>
<el-form :model="form" label-width="80px" label-position="left">
<el-form ref="formRef" :model="form" :rules="formRules" label-width="80px" label-position="left">
<el-form-item label="学号" required>
<el-input v-model="form.id" placeholder="请输入学号" />
<el-input v-model="form.studentNo" placeholder="请输入学号" />
</el-form-item>
<el-form-item label="姓名" required>
<el-input v-model="form.name" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="性别">
<el-radio-group v-model="form.gender">
<el-radio value="男"></el-radio>
<el-radio value="女"></el-radio>
<el-radio :value="1"></el-radio>
<el-radio :value="0"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="班级" required>
<el-select v-model="form.className" placeholder="请选择班级" style="width: 100%">
<el-option label="计算机2021-1班" value="计算机2021-1班" />
<el-option label="软件工程2022-2班" value="软件工程2022-2班" />
<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-select>
</el-form-item>
<el-form-item label="手机号">
<el-input v-model="form.phone" placeholder="请输入手机号" />
<el-form-item label="手机号" prop="phone">
<el-input v-model="form.phone" placeholder="请输入手机号" maxlength="11" />
</el-form-item>
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio :value="1">在读</el-radio>
<el-radio :value="0">离校</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="saveStudent"></el-button>
<el-button type="primary" :loading="submitting" @click="saveStudent"></el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { ref, computed, watch, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getStudentPage, getClassList, addStudent, updateStudent, deleteStudent } from '@/api/info'
const searchKey = ref('')
const classFilter = ref('')
const pageCurrent = ref(1)
const pageSize = ref(10)
const total = ref(0)
const loading = ref(false)
const submitting = ref(false)
const dialogVisible = ref(false)
const editing = ref(false)
const selectedRows = ref([])
const hasSelected = ref(false)
const classList = ref([])
const formRef = ref(null)
const formRules = {
phone: [
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
]
}
const form = ref({
id: '',
id: null,
studentNo: '',
name: '',
gender: '男',
className: '',
phone: ''
gender: 1,
classId: '',
phone: '',
status: 1
})
// classId className
const classMap = computed(() => {
const map = {}
classList.value.forEach(c => { map[c.id] = c.className })
return map
})
const students = ref([])
//
const fetchClassList = async () => {
try {
const res = await getClassList()
if (res?.data) {
classList.value = res.data
}
} catch { /* 班级接口不可用时降级 */ }
}
//
const fetchStudents = async () => {
loading.value = true
try {
const params = {
current: pageCurrent.value,
size: pageSize.value
}
if (searchKey.value) params.keyword = searchKey.value
if (classFilter.value) params.classId = classFilter.value
const res = await getStudentPage(params)
if (res?.code === 200 && res.data) {
const records = (res.data.records || []).map(item => ({
...item,
className: classMap.value[item.classId] || `班级${item.classId}`
}))
students.value = records
total.value = res.data.total || students.value.length
}
} finally {
loading.value = false
}
}
//
let searchTimer = null
watch(searchKey, () => {
clearTimeout(searchTimer)
searchTimer = setTimeout(() => {
pageCurrent.value = 1
fetchStudents()
}, 400)
})
const students = ref([
{ id: '2021001', name: '张三', gender: '男', className: '计算机2021-1班', phone: '138****6789', status: '正常' },
{ id: '2021002', name: '李四', gender: '女', className: '计算机2021-1班', phone: '139****7890', status: '正常' },
{ id: '2021003', name: '王五', gender: '男', className: '计算机2021-1班', phone: '137****8901', status: '正常' },
{ id: '2022001', name: '赵六', gender: '男', className: '软件工程2022-2班', phone: '136****9012', status: '正常' },
{ id: '2022002', name: '钱七', gender: '女', className: '软件工程2022-2班', phone: '135****0123', status: '休学' }
])
watch(classFilter, () => {
pageCurrent.value = 1
fetchStudents()
})
watch(pageCurrent, () => fetchStudents())
onMounted(async () => {
await fetchClassList()
fetchStudents()
})
const handleSelectionChange = (rows) => {
selectedRows.value = rows
@ -121,30 +201,85 @@ const handleSelectionChange = (rows) => {
const showAddDialog = () => {
editing.value = false
form.value = { id: '', name: '', gender: '男', className: '', phone: '' }
form.value = { id: null, studentNo: '', name: '', gender: 1, classId: '', phone: '', status: 1 }
dialogVisible.value = true
formRef.value?.clearValidate()
}
const editStudent = (row) => {
editing.value = true
form.value = { ...row }
form.value = {
id: row.id,
studentNo: row.studentNo,
name: row.name,
gender: row.gender,
classId: row.classId,
phone: row.phone || '',
status: row.status
}
dialogVisible.value = true
formRef.value?.clearValidate()
}
const saveStudent = () => {
ElMessage.success(editing.value ? '编辑成功' : '添加成功')
dialogVisible.value = false
const saveStudent = async () => {
const valid = await formRef.value?.validate().catch(() => false)
if (!valid) return
submitting.value = true
const payload = {
studentNo: form.value.studentNo,
name: form.value.name,
gender: form.value.gender,
classId: form.value.classId,
phone: form.value.phone,
status: form.value.status
}
try {
if (editing.value) {
payload.id = form.value.id
await updateStudent(payload)
} else {
await addStudent(payload)
}
ElMessage.success(editing.value ? '编辑成功' : '添加成功')
dialogVisible.value = false
fetchStudents()
} catch {
ElMessage.error(editing.value ? '编辑失败' : '添加失败')
} finally {
submitting.value = false
}
}
const deleteStudent = (row) => {
const deleteOne = (row) => {
ElMessageBox.confirm(`确认删除学生 ${row.name}`, '提示', { type: 'warning' })
.then(() => ElMessage.success('删除成功'))
.then(async () => {
try {
await deleteStudent([row.id])
ElMessage.success('删除成功')
fetchStudents()
} catch {
ElMessage.error('删除失败')
}
})
.catch(() => {})
}
const batchDelete = () => {
if (selectedRows.value.length === 0) return
ElMessageBox.confirm(`确认删除选中的 ${selectedRows.value.length} 名学生?`, '批量删除', { type: 'warning' })
.then(() => ElMessage.success('批量删除成功'))
.then(async () => {
try {
const ids = selectedRows.value.map(r => r.id)
await deleteStudent(ids)
ElMessage.success('批量删除成功')
selectedRows.value = []
hasSelected.value = false
fetchStudents()
} catch {
ElMessage.error('批量删除失败')
}
})
.catch(() => {})
}
</script>

Loading…
Cancel
Save