feat(student): 添加学生查询功能并支持按姓名过滤

- 学生查询页面新增姓名输入框及查询、重置按钮
- 实现学生列表展示与分页功能
- 支持根据选中班级和年级联动查询学生
- 学生接口新增按姓名条件查询支持
- 服务层及Mapper层方法添加姓名参数过滤功能
- Vue页面同步新增学生相关响应式状态与方法
- 优化表格交互,支持点击行选中高亮及联动查询
This commit is contained in:
lbw
2025-12-15 11:15:47 +08:00
parent 857fa85c74
commit eb2c0d2f6c
8 changed files with 112 additions and 12 deletions

View File

@@ -41,8 +41,8 @@ public class StudentController {
Integer gradeId = findStudentsReqVO.getGradeId(); Integer gradeId = findStudentsReqVO.getGradeId();
Integer pageNo = findStudentsReqVO.getPageNo(); Integer pageNo = findStudentsReqVO.getPageNo();
Integer pageSize = findStudentsReqVO.getPageSize(); Integer pageSize = findStudentsReqVO.getPageSize();
String name = findStudentsReqVO.getName();
List<StudentDO> students = studentService.getStudentsByClassIdAndGradeId(classId, gradeId, pageNo, pageSize); List<StudentDO> students = studentService.getStudentsByClassIdAndGradeId(classId, gradeId, name, pageNo, pageSize);
int allStudents = studentService.getAllStudents(); int allStudents = studentService.getAllStudents();
List<StudentItemRspVO> studentItemRspVOS = students.stream().map(studentDO -> StudentItemRspVO.builder() List<StudentItemRspVO> studentItemRspVOS = students.stream().map(studentDO -> StudentItemRspVO.builder()

View File

@@ -1,12 +1,13 @@
package com.yinlihupo.enlish.service.domain.mapper; package com.yinlihupo.enlish.service.domain.mapper;
import com.yinlihupo.enlish.service.domain.dataobject.StudentDO; import com.yinlihupo.enlish.service.domain.dataobject.StudentDO;
import org.apache.ibatis.annotations.Param;
import java.util.List; import java.util.List;
public interface StudentDOMapper { public interface StudentDOMapper {
List<StudentDO> selectStudentDOListByClassIdAndGradeId(Integer classId, Integer gradeId, Integer pageSize, Integer offset); List<StudentDO> selectStudentDOListByClassIdAndGradeId(@Param("classId") Integer classId, @Param("gradeId") Integer gradeId, @Param("name") String name, @Param("startIndex") Integer startIndex, @Param("pageSize") Integer pageSize);
int selectStudentCount(); int selectStudentCount();

View File

@@ -11,6 +11,7 @@ import lombok.NoArgsConstructor;
@Builder @Builder
public class FindStudentsReqVO { public class FindStudentsReqVO {
private String name;
private Integer classId; private Integer classId;
private Integer gradeId; private Integer gradeId;

View File

@@ -7,7 +7,7 @@ import java.util.List;
public interface StudentService { public interface StudentService {
List<StudentDO> getStudentsByClassIdAndGradeId(Integer classId, Integer gradeId, Integer pageNo, Integer pageSize); List<StudentDO> getStudentsByClassIdAndGradeId(Integer classId, Integer gradeId, String name, Integer pageNo, Integer pageSize);
int getAllStudents(); int getAllStudents();

View File

@@ -16,9 +16,9 @@ public class StudentServiceImpl implements StudentService {
private StudentDOMapper studentDOMapper; private StudentDOMapper studentDOMapper;
@Override @Override
public List<StudentDO> getStudentsByClassIdAndGradeId(Integer classId, Integer gradeId, Integer pageNo, Integer pageSize) { public List<StudentDO> getStudentsByClassIdAndGradeId(Integer classId, Integer gradeId, String name, Integer pageNo, Integer pageSize) {
return studentDOMapper.selectStudentDOListByClassIdAndGradeId(classId, gradeId, pageSize, (pageNo - 1) * pageSize); return studentDOMapper.selectStudentDOListByClassIdAndGradeId(classId, gradeId, name, (pageNo - 1) * pageSize, pageSize);
} }
@Override @Override

View File

@@ -22,7 +22,10 @@
<if test="gradeId != null"> <if test="gradeId != null">
AND grade_id = #{gradeId} AND grade_id = #{gradeId}
</if> </if>
LIMIT #{offset}, #{pageSize} <if test="name != null">
AND name like concat('%', #{name}, '%')
</if>
LIMIT #{startIndex}, #{pageSize}
</select> </select>
<select id="selectStudentCount"> <select id="selectStudentCount">

View File

@@ -5,3 +5,7 @@ export function getStudentDetail(id) {
studentId: id studentId: id
}) })
} }
export function getStudentList(data) {
return axios.post('/student/list', data)
}

View File

@@ -11,7 +11,8 @@
<div class="lg:col-span-1 flex flex-col gap-6"> <div class="lg:col-span-1 flex flex-col gap-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6"> <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
<div class="text-lg font-semibold mb-4">班级列表</div> <div class="text-lg font-semibold mb-4">班级列表</div>
<el-table :data="classes" border class="w-full" v-loading="loading"> <el-table ref="classTableRef" :data="classes" border class="w-full" v-loading="loading" highlight-current-row
row-key="id" :current-row-key="selectedClassId" @row-click="onClassRowClick">
<el-table-column prop="id" label="ID" width="80" /> <el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="title" label="班级名称" min-width="120" /> <el-table-column prop="title" label="班级名称" min-width="120" />
<el-table-column prop="gradeName" label="年级" min-width="120" /> <el-table-column prop="gradeName" label="年级" min-width="120" />
@@ -25,13 +26,33 @@
</div> </div>
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 lg:col-span-1 lg:row-span-2"> <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 lg:col-span-1 lg:row-span-2">
<div class="text-lg font-semibold mb-4">右侧卡片</div> <div class="text-lg font-semibold mb-4">学生查询</div>
<div class="text-gray-500">内容待完善</div> <div class="flex flex-wrap items-center gap-3 mb-4">
<el-input v-model="studentName" placeholder="按姓名查询" clearable style="max-width: 220px" />
<el-tag v-if="selectedClassId" effect="plain">班级{{ selectedClassTitle }} (ID: {{
selectedClassId }})</el-tag>
<el-tag v-if="selectedGradeId" effect="plain">年级{{ selectedGradeTitle }} (ID: {{
selectedGradeId }})</el-tag>
<el-button type="primary" @click="fetchStudents">查询</el-button>
<el-button @click="resetStudentFilters">重置</el-button>
</div>
<el-table :data="students" border class="w-full" v-loading="studentLoading">
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="name" label="姓名" min-width="120" />
<el-table-column prop="classId" label="班级ID" width="100" />
<el-table-column prop="gradeId" label="年级ID" width="100" />
</el-table>
<div class="mt-4 flex justify-end">
<el-pagination background layout="prev, pager, next, sizes, total"
:total="studentTotalCount" :page-size="studentPageSize" :current-page="studentPageNo"
@current-change="handleStudentPageChange" @size-change="handleStudentSizeChange" />
</div>
</div> </div>
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6" v-loading="gradeLoading"> <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6" v-loading="gradeLoading">
<div class="text-lg font-semibold mb-4">年级列表</div> <div class="text-lg font-semibold mb-4">年级列表</div>
<el-table :data="grades" border class="w-full"> <el-table ref="gradeTableRef" :data="grades" border class="w-full" highlight-current-row row-key="id"
:current-row-key="selectedGradeId" @row-click="onGradeRowClick">
<el-table-column prop="id" label="ID" width="80" /> <el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="title" label="年级名称" min-width="160" /> <el-table-column prop="title" label="年级名称" min-width="160" />
</el-table> </el-table>
@@ -54,17 +75,30 @@ import Header from '@/layouts/components/Header.vue'
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { getClassList } from '@/api/class' import { getClassList } from '@/api/class'
import { getGradeList } from '@/api/grade' import { getGradeList } from '@/api/grade'
import { getStudentList } from '@/api/student'
const classes = ref([]) const classes = ref([])
const pageNo = ref(1) const pageNo = ref(1)
const pageSize = ref(10) const pageSize = ref(10)
const totalCount = ref(0) const totalCount = ref(0)
const loading = ref(false) const loading = ref(false)
const classTableRef = ref(null)
const selectedClassId = ref(null)
const selectedClassTitle = ref('')
const grades = ref([]) const grades = ref([])
const gradePageNo = ref(1) const gradePageNo = ref(1)
const gradePageSize = ref(10) const gradePageSize = ref(10)
const gradeTotalCount = ref(0) const gradeTotalCount = ref(0)
const gradeLoading = ref(false) const gradeLoading = ref(false)
const gradeTableRef = ref(null)
const selectedGradeId = ref(null)
const selectedGradeTitle = ref('')
const students = ref([])
const studentPageNo = ref(1)
const studentPageSize = ref(10)
const studentTotalCount = ref(0)
const studentLoading = ref(false)
const studentName = ref('')
async function fetchClasses() { async function fetchClasses() {
loading.value = true loading.value = true
@@ -94,6 +128,27 @@ async function fetchGrades() {
} }
} }
async function fetchStudents() {
studentLoading.value = true
try {
const payload = {
name: studentName.value || null,
classId: selectedClassId.value ?? null,
gradeId: selectedGradeId.value ?? null,
pageNo: studentPageNo.value,
pageSize: studentPageSize.value
}
const res = await getStudentList(payload)
const d = res.data
students.value = Array.isArray(d.data) ? d.data : []
studentTotalCount.value = d.totalCount || 0
studentPageNo.value = d.pageNo || studentPageNo.value
studentPageSize.value = d.pageSize || studentPageSize.value
} finally {
studentLoading.value = false
}
}
function handlePageChange(p) { function handlePageChange(p) {
pageNo.value = p pageNo.value = p
fetchClasses() fetchClasses()
@@ -114,8 +169,44 @@ function handleGradeSizeChange(s) {
fetchGrades() fetchGrades()
} }
function handleStudentPageChange(p) {
studentPageNo.value = p
fetchStudents()
}
function handleStudentSizeChange(s) {
studentPageSize.value = s
studentPageNo.value = 1
fetchStudents()
}
function onClassRowClick(row) {
selectedClassId.value = row.id
selectedClassTitle.value = row.title
classTableRef.value?.setCurrentRow(row)
studentPageNo.value = 1
fetchStudents()
}
function onGradeRowClick(row) {
selectedGradeId.value = row.id
selectedGradeTitle.value = row.title
gradeTableRef.value?.setCurrentRow(row)
studentPageNo.value = 1
fetchStudents()
}
function resetStudentFilters() {
studentName.value = ''
selectedClassId.value = null
selectedClassTitle.value = ''
selectedGradeId.value = null
selectedGradeTitle.value = ''
classTableRef.value?.setCurrentRow()
gradeTableRef.value?.setCurrentRow()
studentPageNo.value = 1
fetchStudents()
}
onMounted(() => { onMounted(() => {
fetchClasses() fetchClasses()
fetchGrades() fetchGrades()
fetchStudents()
}) })
</script> </script>