feat(class): 添加班级删除功能
- 在班级列表表格中新增“操作”列,添加删除按钮 - 实现删除班级的接口调用逻辑 - 删除成功后刷新班级列表 - 删除当前选中班级时清空选中状态 - 添加成功和失败的用户提示信息
This commit is contained in:
@@ -2,6 +2,8 @@ package com.yinlihupo.enlish.service.controller;
|
|||||||
|
|
||||||
import com.yinlihupo.enlish.service.domain.dataobject.ClassDO;
|
import com.yinlihupo.enlish.service.domain.dataobject.ClassDO;
|
||||||
import com.yinlihupo.enlish.service.domain.dataobject.GradeDO;
|
import com.yinlihupo.enlish.service.domain.dataobject.GradeDO;
|
||||||
|
import com.yinlihupo.enlish.service.model.vo.classs.AddClassReqVO;
|
||||||
|
import com.yinlihupo.enlish.service.model.vo.classs.DeleteClassReqVO;
|
||||||
import com.yinlihupo.enlish.service.model.vo.classs.FindClassReqVO;
|
import com.yinlihupo.enlish.service.model.vo.classs.FindClassReqVO;
|
||||||
import com.yinlihupo.enlish.service.model.vo.classs.FindClassRspVO;
|
import com.yinlihupo.enlish.service.model.vo.classs.FindClassRspVO;
|
||||||
import com.yinlihupo.enlish.service.service.ClassService;
|
import com.yinlihupo.enlish.service.service.ClassService;
|
||||||
@@ -41,4 +43,16 @@ public class ClassController {
|
|||||||
|
|
||||||
return PageResponse.success(findClassRspVOS, page, total, pageSize);
|
return PageResponse.success(findClassRspVOS, page, total, pageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("add")
|
||||||
|
@ApiOperationLog(description = "添加班级")
|
||||||
|
public void addClass(@RequestBody AddClassReqVO addClassReqVO) {
|
||||||
|
classService.addClass(addClassReqVO.getTitle(), addClassReqVO.getGradeId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("delete")
|
||||||
|
@ApiOperationLog(description = "删除班级")
|
||||||
|
public void deleteClass(@RequestBody DeleteClassReqVO deleteClassReqVO) {
|
||||||
|
classService.deleteClass(deleteClassReqVO.getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,4 +14,8 @@ public interface ClassDOMapper {
|
|||||||
Integer selectClassDOCount();
|
Integer selectClassDOCount();
|
||||||
|
|
||||||
List<ClassDO> selectClassDOListByIds(@Param("classIds") List<Integer> classIds);
|
List<ClassDO> selectClassDOListByIds(@Param("classIds") List<Integer> classIds);
|
||||||
|
|
||||||
|
void insert(ClassDO classDO);
|
||||||
|
|
||||||
|
void delete(@Param("id") Integer id);
|
||||||
}
|
}
|
||||||
@@ -7,4 +7,10 @@ import java.util.List;
|
|||||||
|
|
||||||
public interface GradeClassDOMapper {
|
public interface GradeClassDOMapper {
|
||||||
List<GradeClassDO> selectByClassIds(@Param("classIds") List<Integer> classIds);
|
List<GradeClassDO> selectByClassIds(@Param("classIds") List<Integer> classIds);
|
||||||
|
|
||||||
|
void insert(@Param("classId") Integer classId, @Param("gradeId") Integer gradeId);
|
||||||
|
|
||||||
|
void deleteByClassId(@Param("classId") Integer classId);
|
||||||
|
|
||||||
|
Integer selectByGradeId(@Param("gradeId") Integer gradeId);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.yinlihupo.enlish.service.model.vo.classs;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class AddClassReqVO {
|
||||||
|
|
||||||
|
private String title;
|
||||||
|
private Integer gradeId;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.yinlihupo.enlish.service.model.vo.classs;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@Data
|
||||||
|
public class DeleteClassReqVO {
|
||||||
|
|
||||||
|
private Integer id;
|
||||||
|
}
|
||||||
@@ -15,4 +15,8 @@ public interface ClassService {
|
|||||||
int getClassCount();
|
int getClassCount();
|
||||||
|
|
||||||
Map<Integer, GradeDO> findClassIds2GradeMap(List<Integer> classIds);
|
Map<Integer, GradeDO> findClassIds2GradeMap(List<Integer> classIds);
|
||||||
|
|
||||||
|
void addClass(String className, Integer gradeId);
|
||||||
|
|
||||||
|
void deleteClass(Integer classId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,4 +70,18 @@ public class ClassServiceImpl implements ClassService {
|
|||||||
HashMap::new // 指定返回 HashMap
|
HashMap::new // 指定返回 HashMap
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addClass(String className, Integer gradeId) {
|
||||||
|
ClassDO classDO = ClassDO.builder().title(className).build();
|
||||||
|
classDOMapper.insert(classDO);
|
||||||
|
Integer id = classDO.getId();
|
||||||
|
|
||||||
|
gradeClassDOMapper.insert(id, gradeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteClass(Integer classId) {
|
||||||
|
classDOMapper.delete(classId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.yinlihupo.enlish.service.service.grade;
|
package com.yinlihupo.enlish.service.service.grade;
|
||||||
|
|
||||||
import com.yinlihupo.enlish.service.domain.dataobject.GradeDO;
|
import com.yinlihupo.enlish.service.domain.dataobject.GradeDO;
|
||||||
|
import com.yinlihupo.enlish.service.domain.mapper.GradeClassDOMapper;
|
||||||
import com.yinlihupo.enlish.service.domain.mapper.GradeDOMapper;
|
import com.yinlihupo.enlish.service.domain.mapper.GradeDOMapper;
|
||||||
import com.yinlihupo.enlish.service.service.GradeService;
|
import com.yinlihupo.enlish.service.service.GradeService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
@@ -13,6 +14,8 @@ public class GradeServiceImpl implements GradeService {
|
|||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private GradeDOMapper gradeDOMapper;
|
private GradeDOMapper gradeDOMapper;
|
||||||
|
@Resource
|
||||||
|
private GradeClassDOMapper gradeClassDOMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GradeDO findByClassId(Integer classId) {
|
public GradeDO findByClassId(Integer classId) {
|
||||||
@@ -36,6 +39,12 @@ public class GradeServiceImpl implements GradeService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete(Integer id) {
|
public void delete(Integer id) {
|
||||||
gradeDOMapper.deleteById(id);
|
|
||||||
|
Integer classIds = gradeClassDOMapper.selectByGradeId(id);
|
||||||
|
if (classIds == null || classIds == 0) {
|
||||||
|
gradeDOMapper.deleteById(id);
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("请先删除与该年级关联的班级");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,5 +32,14 @@
|
|||||||
</foreach>
|
</foreach>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
insert into `class`
|
||||||
|
(title)
|
||||||
|
values (#{title})
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<delete id="delete">
|
||||||
|
delete from `class`
|
||||||
|
where id = #{id}
|
||||||
|
</delete>
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -13,5 +13,20 @@
|
|||||||
#{classId}
|
#{classId}
|
||||||
</foreach>
|
</foreach>
|
||||||
</select>
|
</select>
|
||||||
|
<select id="selectByGradeId" resultType="java.lang.Integer">
|
||||||
|
select count(1)
|
||||||
|
from grade_class
|
||||||
|
where grade_id = #{gradeId}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<insert id="insert">
|
||||||
|
insert into grade_class
|
||||||
|
(class_id, grade_id)
|
||||||
|
values
|
||||||
|
(#{classId}, #{gradeId})
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<delete id="deleteByClassId">
|
||||||
|
delete from grade_class where class_id = #{classId}
|
||||||
|
</delete>
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -6,3 +6,16 @@ export function getClassList(page, size) {
|
|||||||
pageSize: size
|
pageSize: size
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function addClass(name, gradeId) {
|
||||||
|
return axios.post('/class/add', {
|
||||||
|
title: name,
|
||||||
|
gradeId: gradeId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteClass(id) {
|
||||||
|
return axios.post('/class/delete', {
|
||||||
|
id: id
|
||||||
|
})
|
||||||
|
}
|
||||||
86
enlish-vue/src/layouts/components/AddClassDialog.vue
Normal file
86
enlish-vue/src/layouts/components/AddClassDialog.vue
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog v-model="visible" title="新增班级" width="480px" :close-on-click-modal="false">
|
||||||
|
<div class="space-y-4" v-loading="loading">
|
||||||
|
<el-form label-width="80px">
|
||||||
|
<el-form-item label="班级名称">
|
||||||
|
<el-input v-model="name" placeholder="请输入班级名称,如:二班" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="年级">
|
||||||
|
<el-select v-model="gradeId" placeholder="请选择年级" style="width: 260px">
|
||||||
|
<el-option v-for="g in gradeOptions" :key="g.id" :label="g.title" :value="g.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<div class="flex justify-end gap-2">
|
||||||
|
<el-button @click="visible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :disabled="!canSubmit" @click="handleSubmit">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, watch } from 'vue'
|
||||||
|
import { getGradeList } from '@/api/grade'
|
||||||
|
import { addClass } from '@/api/class'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: { type: Boolean, default: false },
|
||||||
|
defaultGradeId: { type: [Number, String], default: null }
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['update:modelValue', 'success'])
|
||||||
|
|
||||||
|
const visible = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (val) => emit('update:modelValue', val)
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const name = ref('')
|
||||||
|
const gradeId = ref(null)
|
||||||
|
const gradeOptions = ref([])
|
||||||
|
|
||||||
|
const canSubmit = computed(() => name.value.trim().length > 0 && !!gradeId.value)
|
||||||
|
|
||||||
|
async function fetchGrades() {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await getGradeList(1, 100)
|
||||||
|
const d = res?.data
|
||||||
|
gradeOptions.value = Array.isArray(d?.data) ? d.data : []
|
||||||
|
if (props.defaultGradeId && !gradeId.value) {
|
||||||
|
gradeId.value = Number(props.defaultGradeId)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
if (!canSubmit.value) return
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
await addClass(name.value.trim(), Number(gradeId.value))
|
||||||
|
ElMessage.success('新增班级成功')
|
||||||
|
emit('success')
|
||||||
|
visible.value = false
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(v) => {
|
||||||
|
if (v) {
|
||||||
|
name.value = ''
|
||||||
|
gradeId.value = props.defaultGradeId ? Number(props.defaultGradeId) : null
|
||||||
|
fetchGrades()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -16,12 +16,22 @@
|
|||||||
<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" />
|
||||||
|
<el-table-column label="操作" width="120" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button type="danger" size="small" @click.stop="onDeleteClass(row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<div class="mt-4 flex justify-end">
|
<div class="mt-4 flex justify-end">
|
||||||
<el-pagination background layout="prev, pager, next, sizes, total" :total="totalCount"
|
<el-pagination background layout="prev, pager, next, sizes, total" :total="totalCount"
|
||||||
:page-size="pageSize" :current-page="pageNo" @current-change="handlePageChange"
|
:page-size="pageSize" :current-page="pageNo" @current-change="handlePageChange"
|
||||||
@size-change="handleSizeChange" />
|
@size-change="handleSizeChange" />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mt-3 flex justify-end">
|
||||||
|
<el-button type="primary" @click="showAddClassDialog = true">新增班级</el-button>
|
||||||
|
</div>
|
||||||
|
<AddClassDialog v-model="showAddClassDialog" :default-grade-id="selectedGradeId"
|
||||||
|
@success="fetchClasses" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -35,18 +45,13 @@
|
|||||||
selectedGradeId }})</el-tag>
|
selectedGradeId }})</el-tag>
|
||||||
<el-button type="primary" @click="fetchStudents">查询</el-button>
|
<el-button type="primary" @click="fetchStudents">查询</el-button>
|
||||||
<el-button @click="resetStudentFilters">重置</el-button>
|
<el-button @click="resetStudentFilters">重置</el-button>
|
||||||
<el-button type="success" :disabled="selectedStudentIds.length === 0" @click="showGenerateDialog = true">
|
<el-button type="success" :disabled="selectedStudentIds.length === 0"
|
||||||
|
@click="showGenerateDialog = true">
|
||||||
生成试题
|
生成试题
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
<el-table
|
<el-table ref="studentTableRef" :data="students" border class="w-full"
|
||||||
ref="studentTableRef"
|
v-loading="studentLoading" @selection-change="onStudentSelectionChange">
|
||||||
:data="students"
|
|
||||||
border
|
|
||||||
class="w-full"
|
|
||||||
v-loading="studentLoading"
|
|
||||||
@selection-change="onStudentSelectionChange"
|
|
||||||
>
|
|
||||||
<el-table-column type="selection" width="48" />
|
<el-table-column type="selection" width="48" />
|
||||||
<el-table-column prop="id" label="ID" width="80" />
|
<el-table-column prop="id" label="ID" width="80" />
|
||||||
<el-table-column prop="name" label="姓名" min-width="120" />
|
<el-table-column prop="name" label="姓名" min-width="120" />
|
||||||
@@ -58,22 +63,20 @@
|
|||||||
:total="studentTotalCount" :page-size="studentPageSize" :current-page="studentPageNo"
|
:total="studentTotalCount" :page-size="studentPageSize" :current-page="studentPageNo"
|
||||||
@current-change="handleStudentPageChange" @size-change="handleStudentSizeChange" />
|
@current-change="handleStudentPageChange" @size-change="handleStudentSizeChange" />
|
||||||
</div>
|
</div>
|
||||||
<ExamGenerateDialog
|
<ExamGenerateDialog v-model="showGenerateDialog" :student-ids="selectedStudentIds"
|
||||||
v-model="showGenerateDialog"
|
:default-grade-id="selectedGradeId" />
|
||||||
:student-ids="selectedStudentIds"
|
|
||||||
:default-grade-id="selectedGradeId"
|
|
||||||
/>
|
|
||||||
</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 ref="gradeTableRef" :data="grades" border class="w-full" highlight-current-row row-key="id"
|
<el-table ref="gradeTableRef" :data="grades" border class="w-full" highlight-current-row
|
||||||
:current-row-key="selectedGradeId" @row-click="onGradeRowClick">
|
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-column label="操作" width="120" fixed="right">
|
<el-table-column label="操作" width="120" fixed="right">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button type="danger" size="small" @click.stop="onDeleteGrade(row)">删除</el-button>
|
<el-button type="danger" size="small"
|
||||||
|
@click.stop="onDeleteGrade(row)">删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -98,10 +101,11 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import Header from '@/layouts/components/Header.vue'
|
import Header from '@/layouts/components/Header.vue'
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { getClassList } from '@/api/class'
|
import { getClassList, deleteClass } from '@/api/class'
|
||||||
import { getGradeList, deleteGrade } from '@/api/grade'
|
import { getGradeList } from '@/api/grade'
|
||||||
import { getStudentList } from '@/api/student'
|
import { getStudentList } from '@/api/student'
|
||||||
import ExamGenerateDialog from '@/layouts/components/ExamGenerateDialog.vue'
|
import ExamGenerateDialog from '@/layouts/components/ExamGenerateDialog.vue'
|
||||||
|
import AddClassDialog from '@/layouts/components/AddClassDialog.vue'
|
||||||
import AddGradeDialog from '@/layouts/components/AddGradeDialog.vue'
|
import AddGradeDialog from '@/layouts/components/AddGradeDialog.vue'
|
||||||
|
|
||||||
const classes = ref([])
|
const classes = ref([])
|
||||||
@@ -112,6 +116,7 @@ const loading = ref(false)
|
|||||||
const classTableRef = ref(null)
|
const classTableRef = ref(null)
|
||||||
const selectedClassId = ref(null)
|
const selectedClassId = ref(null)
|
||||||
const selectedClassTitle = ref('')
|
const selectedClassTitle = ref('')
|
||||||
|
const showAddClassDialog = ref(false)
|
||||||
|
|
||||||
const grades = ref([])
|
const grades = ref([])
|
||||||
const gradePageNo = ref(1)
|
const gradePageNo = ref(1)
|
||||||
@@ -228,6 +233,20 @@ function onGradeRowClick(row) {
|
|||||||
studentPageNo.value = 1
|
studentPageNo.value = 1
|
||||||
fetchStudents()
|
fetchStudents()
|
||||||
}
|
}
|
||||||
|
async function onDeleteClass(row) {
|
||||||
|
try {
|
||||||
|
await deleteClass(row.id)
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
if (selectedClassId.value === row.id) {
|
||||||
|
selectedClassId.value = null
|
||||||
|
selectedClassTitle.value = ''
|
||||||
|
classTableRef.value?.setCurrentRow()
|
||||||
|
}
|
||||||
|
await fetchClasses()
|
||||||
|
} catch (e) {
|
||||||
|
ElMessage.error('删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
function resetStudentFilters() {
|
function resetStudentFilters() {
|
||||||
studentName.value = ''
|
studentName.value = ''
|
||||||
selectedClassId.value = null
|
selectedClassId.value = null
|
||||||
|
|||||||
Reference in New Issue
Block a user