feat: 新增学校自动添加年级 + 修复区域层级查询 + 清理区域数据
1. 新增学校时根据学段自动添加对应年级 2. 修复选择省/市时无法显示学校的bug(支持区域层级查询) 3. 区域树默认展开湖北省,平行显示市级 4. 新增区域数据清理SQL脚本(仅保留湖北省)
This commit is contained in:
parent
72cb6668f3
commit
add00c9992
|
|
@ -1,6 +1,7 @@
|
||||||
package org.dromara.pangu.base.controller;
|
package org.dromara.pangu.base.controller;
|
||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.dromara.common.core.domain.R;
|
import org.dromara.common.core.domain.R;
|
||||||
import org.dromara.common.log.annotation.Log;
|
import org.dromara.common.log.annotation.Log;
|
||||||
|
|
@ -13,6 +14,7 @@ import org.dromara.pangu.base.service.IPgGradeService;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -39,9 +41,16 @@ public class PgGradeController extends BaseController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询年级列表(不分页)
|
* 查询年级列表(不分页)
|
||||||
|
* @param grade 查询条件
|
||||||
|
* @param stages 学段列表,逗号分隔(如:1,2 表示小学和初中)
|
||||||
*/
|
*/
|
||||||
@GetMapping("/listAll")
|
@GetMapping("/listAll")
|
||||||
public R<List<PgGrade>> listAll(PgGrade grade) {
|
public R<List<PgGrade>> listAll(PgGrade grade,
|
||||||
|
@RequestParam(required = false) String stages) {
|
||||||
|
if (StrUtil.isNotBlank(stages)) {
|
||||||
|
List<String> stageList = Arrays.asList(stages.split(","));
|
||||||
|
return R.ok(gradeService.selectListByStages(stageList));
|
||||||
|
}
|
||||||
return R.ok(gradeService.selectList(grade));
|
return R.ok(gradeService.selectList(grade));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,4 +42,10 @@ public interface IPgGradeService {
|
||||||
* 批量删除年级
|
* 批量删除年级
|
||||||
*/
|
*/
|
||||||
int deleteByIds(Long[] gradeIds);
|
int deleteByIds(Long[] gradeIds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据学段列表查询年级
|
||||||
|
* @param stages 学段列表(1小学 2初中 3高中 4中专 5大学)
|
||||||
|
*/
|
||||||
|
List<PgGrade> selectListByStages(List<String> stages);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,19 @@ public class PgGradeServiceImpl implements IPgGradeService {
|
||||||
return baseMapper.deleteByIds(Arrays.asList(gradeIds));
|
return baseMapper.deleteByIds(Arrays.asList(gradeIds));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PgGrade> selectListByStages(List<String> stages) {
|
||||||
|
if (stages == null || stages.isEmpty()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
return baseMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<PgGrade>()
|
||||||
|
.in(PgGrade::getStage, stages)
|
||||||
|
.eq(PgGrade::getStatus, "0")
|
||||||
|
.orderByAsc(PgGrade::getOrderNum)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private LambdaQueryWrapper<PgGrade> buildQueryWrapper(PgGrade grade) {
|
private LambdaQueryWrapper<PgGrade> buildQueryWrapper(PgGrade grade) {
|
||||||
LambdaQueryWrapper<PgGrade> lqw = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<PgGrade> lqw = new LambdaQueryWrapper<>();
|
||||||
lqw.like(StrUtil.isNotBlank(grade.getGradeName()), PgGrade::getGradeName, grade.getGradeName());
|
lqw.like(StrUtil.isNotBlank(grade.getGradeName()), PgGrade::getGradeName, grade.getGradeName());
|
||||||
|
|
|
||||||
|
|
@ -60,4 +60,10 @@ public class PgSchool extends BaseEntity {
|
||||||
*/
|
*/
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private String regionName;
|
private String regionName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始年级ID列表(非数据库字段,用于新增时批量添加年级)
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private java.util.List<Long> gradeIds;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,14 @@ public class PgSchoolServiceImpl implements IPgSchoolService {
|
||||||
school.setDeptId(deptBo.getDeptId());
|
school.setDeptId(deptBo.getDeptId());
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseMapper.insert(school);
|
int result = baseMapper.insert(school);
|
||||||
|
|
||||||
|
// 如果传入了初始年级ID列表,批量添加到学校
|
||||||
|
if (school.getGradeIds() != null && !school.getGradeIds().isEmpty()) {
|
||||||
|
addSchoolGrades(school.getSchoolId(), school.getGradeIds());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -295,11 +302,39 @@ public class PgSchoolServiceImpl implements IPgSchoolService {
|
||||||
LambdaQueryWrapper<PgSchool> lqw = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<PgSchool> lqw = new LambdaQueryWrapper<>();
|
||||||
lqw.like(StrUtil.isNotBlank(school.getSchoolName()), PgSchool::getSchoolName, school.getSchoolName());
|
lqw.like(StrUtil.isNotBlank(school.getSchoolName()), PgSchool::getSchoolName, school.getSchoolName());
|
||||||
lqw.eq(StrUtil.isNotBlank(school.getSchoolType()), PgSchool::getSchoolType, school.getSchoolType());
|
lqw.eq(StrUtil.isNotBlank(school.getSchoolType()), PgSchool::getSchoolType, school.getSchoolType());
|
||||||
lqw.eq(school.getRegionId() != null, PgSchool::getRegionId, school.getRegionId());
|
|
||||||
|
// 区域查询:支持查询选定区域及其所有下级区域的学校
|
||||||
|
if (school.getRegionId() != null) {
|
||||||
|
List<Long> regionIds = getRegionIdsWithChildren(school.getRegionId());
|
||||||
|
if (!regionIds.isEmpty()) {
|
||||||
|
lqw.in(PgSchool::getRegionId, regionIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lqw.eq(StrUtil.isNotBlank(school.getStatus()), PgSchool::getStatus, school.getStatus());
|
lqw.eq(StrUtil.isNotBlank(school.getStatus()), PgSchool::getStatus, school.getStatus());
|
||||||
return lqw;
|
return lqw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定区域及其所有下级区域的ID列表
|
||||||
|
* @param regionId 区域ID
|
||||||
|
* @return 包含该区域及所有下级区域的ID列表
|
||||||
|
*/
|
||||||
|
private List<Long> getRegionIdsWithChildren(Long regionId) {
|
||||||
|
List<Long> result = new ArrayList<>();
|
||||||
|
result.add(regionId);
|
||||||
|
|
||||||
|
// 递归查询下级区域
|
||||||
|
List<PgRegion> children = regionMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<PgRegion>().eq(PgRegion::getParentId, regionId)
|
||||||
|
);
|
||||||
|
for (PgRegion child : children) {
|
||||||
|
result.addAll(getRegionIdsWithChildren(child.getRegionId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SchoolTreeNode> selectSchoolTree(PgSchool school) {
|
public List<SchoolTreeNode> selectSchoolTree(PgSchool school) {
|
||||||
// 查询学校列表(使用带数据权限的查询方法)
|
// 查询学校列表(使用带数据权限的查询方法)
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,26 @@
|
||||||
inactive-text="停用"
|
inactive-text="停用"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<!-- 新增时显示年级选择区域 -->
|
||||||
|
<el-form-item v-if="!isEdit && gradeList.length > 0" label="初始年级">
|
||||||
|
<div class="grade-select-wrapper">
|
||||||
|
<el-checkbox-group v-model="selectedGradeIds">
|
||||||
|
<el-checkbox
|
||||||
|
v-for="grade in gradeList"
|
||||||
|
:key="grade.gradeId"
|
||||||
|
:value="grade.gradeId"
|
||||||
|
:label="grade.gradeId"
|
||||||
|
>
|
||||||
|
{{ grade.gradeName }}
|
||||||
|
</el-checkbox>
|
||||||
|
</el-checkbox-group>
|
||||||
|
<div class="grade-tip">
|
||||||
|
<el-text type="info" size="small">
|
||||||
|
已选择 {{ selectedGradeIds.length }} 个年级,将在学校创建后自动添加
|
||||||
|
</el-text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="dialogVisible = false">取消</el-button>
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
|
|
@ -77,9 +97,10 @@
|
||||||
* @author pangu
|
* @author pangu
|
||||||
*/
|
*/
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { computed, ref, onMounted } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
import { addSchool, updateSchool } from '@/api/pangu/school'
|
import { addSchool, updateSchool } from '@/api/pangu/school'
|
||||||
import { deptTreeSelect } from '@/api/system/user'
|
import { deptTreeSelect } from '@/api/system/user'
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 接收父组件传递的区域树
|
// 接收父组件传递的区域树
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|
@ -99,6 +120,10 @@ const submitLoading = ref(false)
|
||||||
const formRef = ref(null)
|
const formRef = ref(null)
|
||||||
const isEdit = ref(false)
|
const isEdit = ref(false)
|
||||||
|
|
||||||
|
// 年级列表和选中的年级ID
|
||||||
|
const gradeList = ref([])
|
||||||
|
const selectedGradeIds = ref([])
|
||||||
|
|
||||||
// 表单数据
|
// 表单数据
|
||||||
const form = ref({
|
const form = ref({
|
||||||
schoolId: null,
|
schoolId: null,
|
||||||
|
|
@ -159,6 +184,51 @@ const handleRegionChange = (value) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 学校类型对应学段映射
|
||||||
|
const getStagesBySchoolType = (schoolType) => {
|
||||||
|
const map = {
|
||||||
|
'1': ['1'], // 小学
|
||||||
|
'2': ['2'], // 初中
|
||||||
|
'3': ['3'], // 高中
|
||||||
|
'4': ['1', '2'], // 九年一贯制
|
||||||
|
'5': ['2', '3'] // 完全中学
|
||||||
|
}
|
||||||
|
return map[schoolType] || []
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据学校类型加载年级列表
|
||||||
|
const loadGradesBySchoolType = async (schoolType) => {
|
||||||
|
const stages = getStagesBySchoolType(schoolType)
|
||||||
|
if (stages.length === 0) {
|
||||||
|
gradeList.value = []
|
||||||
|
selectedGradeIds.value = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await request.get('/business/grade/listAll', {
|
||||||
|
params: { stages: stages.join(',') }
|
||||||
|
})
|
||||||
|
if (res.code === 200) {
|
||||||
|
gradeList.value = res.data || []
|
||||||
|
// 默认全选
|
||||||
|
selectedGradeIds.value = gradeList.value.map(g => g.gradeId)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载年级列表失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听学校类型变化,自动加载对应年级(仅新增模式)
|
||||||
|
watch(() => form.value.schoolType, async (newType) => {
|
||||||
|
if (!isEdit.value && newType) {
|
||||||
|
await loadGradesBySchoolType(newType)
|
||||||
|
} else if (!newType) {
|
||||||
|
gradeList.value = []
|
||||||
|
selectedGradeIds.value = []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// 加载部门树
|
// 加载部门树
|
||||||
const loadDeptTree = async () => {
|
const loadDeptTree = async () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -208,6 +278,9 @@ const open = async (row, defaultRegionId = null) => {
|
||||||
regionName: defaultRegionId ? getRegionPath(regionIds, props.regionTree) : '',
|
regionName: defaultRegionId ? getRegionPath(regionIds, props.regionTree) : '',
|
||||||
status: '0'
|
status: '0'
|
||||||
}
|
}
|
||||||
|
// 重置年级选择
|
||||||
|
gradeList.value = []
|
||||||
|
selectedGradeIds.value = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,7 +319,8 @@ const handleSubmit = async () => {
|
||||||
try {
|
try {
|
||||||
const submitData = {
|
const submitData = {
|
||||||
...form.value,
|
...form.value,
|
||||||
regionIds: undefined // 不提交级联选择器的数组
|
regionIds: undefined, // 不提交级联选择器的数组
|
||||||
|
gradeIds: isEdit.value ? undefined : selectedGradeIds.value // 新增时携带年级ID列表
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = form.value.schoolId
|
const res = form.value.schoolId
|
||||||
|
|
@ -254,7 +328,11 @@ const handleSubmit = async () => {
|
||||||
: await addSchool(submitData)
|
: await addSchool(submitData)
|
||||||
|
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
ElMessage.success(isEdit.value ? '修改成功' : '新增成功')
|
const gradeCount = selectedGradeIds.value.length
|
||||||
|
const msg = isEdit.value
|
||||||
|
? '修改成功'
|
||||||
|
: (gradeCount > 0 ? `新增成功,已自动添加 ${gradeCount} 个年级` : '新增成功')
|
||||||
|
ElMessage.success(msg)
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
emit('success')
|
emit('success')
|
||||||
}
|
}
|
||||||
|
|
@ -269,3 +347,16 @@ const handleSubmit = async () => {
|
||||||
// 暴露方法给父组件
|
// 暴露方法给父组件
|
||||||
defineExpose({ open })
|
defineExpose({ open })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.grade-select-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.grade-select-wrapper .el-checkbox {
|
||||||
|
margin-right: 16px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.grade-tip {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
:props="{ label: 'regionName', children: 'children' }"
|
:props="{ label: 'regionName', children: 'children' }"
|
||||||
node-key="regionId"
|
node-key="regionId"
|
||||||
highlight-current
|
highlight-current
|
||||||
|
:default-expanded-keys="defaultExpandedKeys"
|
||||||
:filter-node-method="filterNode"
|
:filter-node-method="filterNode"
|
||||||
@node-click="handleNodeClick"
|
@node-click="handleNodeClick"
|
||||||
/>
|
/>
|
||||||
|
|
@ -140,6 +141,8 @@ const treeRef = ref()
|
||||||
const treeFilterText = ref('')
|
const treeFilterText = ref('')
|
||||||
const regionTree = ref([])
|
const regionTree = ref([])
|
||||||
const selectedRegionId = ref(null)
|
const selectedRegionId = ref(null)
|
||||||
|
// 默认展开湖北省(regionId=42),只展开一级显示市级
|
||||||
|
const defaultExpandedKeys = ref([42])
|
||||||
|
|
||||||
// 表格相关
|
// 表格相关
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
-- ================================================================
|
||||||
|
-- V1.0.5 清理非湖北省区域数据
|
||||||
|
-- 执行说明:先检查湖北省的region_id,确认为42后再执行删除
|
||||||
|
-- 执行时间:需在业务低峰期执行
|
||||||
|
-- ================================================================
|
||||||
|
|
||||||
|
-- 0. 首先确认湖北省的region_id
|
||||||
|
SELECT region_id, region_name, parent_id, ancestors FROM pg_region WHERE region_name = '湖北省';
|
||||||
|
-- 预期结果:region_id = 42
|
||||||
|
|
||||||
|
-- 1. 查看要删除的数据量(预检查,不会执行删除)
|
||||||
|
SELECT COUNT(*) as '待删除的区域数量' FROM pg_region
|
||||||
|
WHERE region_id != 42
|
||||||
|
AND ancestors NOT LIKE '0,42%';
|
||||||
|
|
||||||
|
-- 2. 物理删除非湖北省的区域数据
|
||||||
|
-- 删除所有ancestors不以"0,42"开头的区域(即非湖北省及其下级区域)
|
||||||
|
-- 同时排除湖北省本身(region_id=42)
|
||||||
|
DELETE FROM pg_region
|
||||||
|
WHERE region_id != 42
|
||||||
|
AND ancestors NOT LIKE '0,42%';
|
||||||
|
|
||||||
|
-- 3. 删除其他省份的根节点(parent_id = 0 且 region_id != 42)
|
||||||
|
DELETE FROM pg_region
|
||||||
|
WHERE parent_id = 0
|
||||||
|
AND region_id != 42;
|
||||||
|
|
||||||
|
-- 4. 验证结果
|
||||||
|
SELECT COUNT(*) as '剩余区域数量' FROM pg_region;
|
||||||
|
SELECT region_name, level, COUNT(*) as '数量' FROM pg_region GROUP BY level, region_name LIMIT 20;
|
||||||
Loading…
Reference in New Issue