2026-02-02 16:04:47 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<el-dialog
|
|
|
|
|
|
v-model="dialogVisible"
|
|
|
|
|
|
:title="dialogTitle"
|
|
|
|
|
|
width="600px"
|
|
|
|
|
|
:close-on-click-modal="false"
|
|
|
|
|
|
destroy-on-close
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-form
|
|
|
|
|
|
ref="formRef"
|
|
|
|
|
|
:model="form"
|
|
|
|
|
|
:rules="rules"
|
|
|
|
|
|
label-width="100px"
|
|
|
|
|
|
>
|
2026-02-02 17:31:27 +08:00
|
|
|
|
<el-form-item v-if="isEdit" label="学校编码">
|
|
|
|
|
|
<el-input v-model="form.schoolCode" disabled />
|
|
|
|
|
|
</el-form-item>
|
2026-02-02 16:04:47 +08:00
|
|
|
|
<el-form-item label="学校名称" prop="schoolName">
|
2026-02-02 17:31:27 +08:00
|
|
|
|
<el-input v-model="form.schoolName" placeholder="请输入学校名称" maxlength="100" show-word-limit />
|
2026-02-02 16:04:47 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="学校类型" prop="schoolType">
|
|
|
|
|
|
<el-select v-model="form.schoolType" placeholder="请选择学校类型" style="width: 100%">
|
2026-02-02 17:27:00 +08:00
|
|
|
|
<el-option label="小学" value="1" />
|
|
|
|
|
|
<el-option label="初中" value="2" />
|
|
|
|
|
|
<el-option label="高中" value="3" />
|
2026-02-06 10:17:21 +08:00
|
|
|
|
<el-option label="中专" value="4" />
|
|
|
|
|
|
<el-option label="大学" value="5" />
|
2026-02-02 16:04:47 +08:00
|
|
|
|
</el-select>
|
|
|
|
|
|
</el-form-item>
|
2026-02-03 21:45:14 +08:00
|
|
|
|
<el-form-item label="上级部门" prop="deptId">
|
|
|
|
|
|
<el-tree-select
|
|
|
|
|
|
v-model="form.deptId"
|
|
|
|
|
|
:data="deptTree"
|
|
|
|
|
|
:props="{ value: 'id', label: 'label', children: 'children' }"
|
|
|
|
|
|
value-key="id"
|
|
|
|
|
|
placeholder="请选择上级部门(分公司)"
|
|
|
|
|
|
check-strictly
|
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
2026-02-02 16:04:47 +08:00
|
|
|
|
<el-form-item label="所属区域" prop="regionId">
|
|
|
|
|
|
<el-cascader
|
|
|
|
|
|
v-model="form.regionIds"
|
|
|
|
|
|
:options="regionTree"
|
|
|
|
|
|
:props="{
|
|
|
|
|
|
value: 'regionId',
|
|
|
|
|
|
label: 'regionName',
|
|
|
|
|
|
children: 'children',
|
|
|
|
|
|
checkStrictly: true
|
|
|
|
|
|
}"
|
|
|
|
|
|
placeholder="请选择所属区域"
|
|
|
|
|
|
clearable
|
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
|
@change="handleRegionChange"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="状态" prop="status">
|
|
|
|
|
|
<el-switch
|
|
|
|
|
|
v-model="form.status"
|
|
|
|
|
|
active-value="0"
|
|
|
|
|
|
inactive-value="1"
|
|
|
|
|
|
active-text="正常"
|
|
|
|
|
|
inactive-text="停用"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
2026-02-05 11:21:58 +08:00
|
|
|
|
<!-- 新增时显示年级选择区域 -->
|
|
|
|
|
|
<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>
|
2026-02-02 16:04:47 +08:00
|
|
|
|
</el-form>
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
<el-button @click="dialogVisible = false">取消</el-button>
|
|
|
|
|
|
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">确定</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 学校新增/编辑弹窗
|
|
|
|
|
|
* @author pangu
|
|
|
|
|
|
*/
|
|
|
|
|
|
import { ElMessage } from 'element-plus'
|
2026-02-05 11:21:58 +08:00
|
|
|
|
import { computed, ref, watch } from 'vue'
|
2026-02-02 16:04:47 +08:00
|
|
|
|
import { addSchool, updateSchool } from '@/api/pangu/school'
|
2026-02-03 21:45:14 +08:00
|
|
|
|
import { deptTreeSelect } from '@/api/system/user'
|
2026-02-05 11:21:58 +08:00
|
|
|
|
import request from '@/utils/request'
|
2026-02-02 16:04:47 +08:00
|
|
|
|
|
|
|
|
|
|
// 接收父组件传递的区域树
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
|
regionTree: {
|
|
|
|
|
|
type: Array,
|
|
|
|
|
|
default: () => []
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-02-03 21:45:14 +08:00
|
|
|
|
// 部门树数据
|
|
|
|
|
|
const deptTree = ref([])
|
|
|
|
|
|
|
2026-02-02 16:04:47 +08:00
|
|
|
|
const emit = defineEmits(['success'])
|
|
|
|
|
|
|
|
|
|
|
|
const dialogVisible = ref(false)
|
|
|
|
|
|
const submitLoading = ref(false)
|
|
|
|
|
|
const formRef = ref(null)
|
|
|
|
|
|
const isEdit = ref(false)
|
|
|
|
|
|
|
2026-02-05 11:21:58 +08:00
|
|
|
|
// 年级列表和选中的年级ID
|
|
|
|
|
|
const gradeList = ref([])
|
|
|
|
|
|
const selectedGradeIds = ref([])
|
|
|
|
|
|
|
2026-02-02 16:04:47 +08:00
|
|
|
|
// 表单数据
|
|
|
|
|
|
const form = ref({
|
2026-02-02 16:32:33 +08:00
|
|
|
|
schoolId: null,
|
2026-02-02 17:31:27 +08:00
|
|
|
|
schoolCode: '',
|
2026-02-02 16:04:47 +08:00
|
|
|
|
schoolName: '',
|
|
|
|
|
|
schoolType: '',
|
2026-02-03 21:45:14 +08:00
|
|
|
|
deptId: null,
|
2026-02-02 16:04:47 +08:00
|
|
|
|
regionId: null,
|
|
|
|
|
|
regionIds: [],
|
|
|
|
|
|
regionName: '',
|
|
|
|
|
|
status: '0'
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-02-02 17:41:22 +08:00
|
|
|
|
// 弹窗标题 - 根据需求文档:编辑-学校
|
|
|
|
|
|
const dialogTitle = computed(() => isEdit.value ? '编辑-学校' : '新增学校')
|
2026-02-02 16:04:47 +08:00
|
|
|
|
|
|
|
|
|
|
// 表单验证规则
|
|
|
|
|
|
const rules = {
|
|
|
|
|
|
schoolName: [
|
|
|
|
|
|
{ required: true, message: '请输入学校名称', trigger: 'blur' }
|
|
|
|
|
|
],
|
|
|
|
|
|
schoolType: [
|
|
|
|
|
|
{ required: true, message: '请选择学校类型', trigger: 'change' }
|
|
|
|
|
|
],
|
2026-02-03 21:45:14 +08:00
|
|
|
|
deptId: [
|
|
|
|
|
|
{ required: true, message: '请选择上级部门', trigger: 'change' }
|
|
|
|
|
|
],
|
2026-02-02 16:04:47 +08:00
|
|
|
|
regionId: [
|
|
|
|
|
|
{ required: true, message: '请选择所属区域', trigger: 'change' }
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取区域完整路径名称
|
|
|
|
|
|
const getRegionPath = (ids, tree) => {
|
|
|
|
|
|
const names = []
|
|
|
|
|
|
const findPath = (nodes, targetId, path) => {
|
|
|
|
|
|
for (const node of nodes) {
|
|
|
|
|
|
if (ids.includes(node.regionId)) {
|
|
|
|
|
|
path.push(node.regionName)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (node.children) {
|
|
|
|
|
|
findPath(node.children, targetId, path)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
findPath(tree, ids[ids.length - 1], names)
|
|
|
|
|
|
return names.join('/')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 区域选择变化
|
|
|
|
|
|
const handleRegionChange = (value) => {
|
|
|
|
|
|
if (value && value.length > 0) {
|
|
|
|
|
|
form.value.regionId = value[value.length - 1]
|
|
|
|
|
|
form.value.regionName = getRegionPath(value, props.regionTree)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
form.value.regionId = null
|
|
|
|
|
|
form.value.regionName = ''
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 10:17:21 +08:00
|
|
|
|
// 学校类型对应学段映射(与 pg_grade.stage 一致:1小学 2初中 3高中 4中专 5大学)
|
2026-02-05 11:21:58 +08:00
|
|
|
|
const getStagesBySchoolType = (schoolType) => {
|
|
|
|
|
|
const map = {
|
2026-02-06 10:17:21 +08:00
|
|
|
|
'1': ['1'], // 小学
|
|
|
|
|
|
'2': ['2'], // 初中
|
|
|
|
|
|
'3': ['3'], // 高中
|
|
|
|
|
|
'4': ['4'], // 中专
|
|
|
|
|
|
'5': ['5'] // 大学
|
2026-02-05 11:21:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
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 = []
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-02-03 21:45:14 +08:00
|
|
|
|
// 加载部门树
|
|
|
|
|
|
const loadDeptTree = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await deptTreeSelect()
|
|
|
|
|
|
if (res.code === 200) {
|
|
|
|
|
|
deptTree.value = res.data
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('加载部门树失败:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-02 16:04:47 +08:00
|
|
|
|
// 打开弹窗
|
2026-02-02 17:41:22 +08:00
|
|
|
|
// row: 编辑时传入学校数据,新增时为null
|
|
|
|
|
|
// defaultRegionId: 新增时默认选中的区域ID(从列表页带入)
|
2026-02-03 21:45:14 +08:00
|
|
|
|
const open = async (row, defaultRegionId = null) => {
|
2026-02-02 16:04:47 +08:00
|
|
|
|
dialogVisible.value = true
|
|
|
|
|
|
isEdit.value = !!row
|
|
|
|
|
|
|
2026-02-03 21:45:14 +08:00
|
|
|
|
// 加载部门树
|
|
|
|
|
|
await loadDeptTree()
|
|
|
|
|
|
|
2026-02-02 16:04:47 +08:00
|
|
|
|
if (row) {
|
|
|
|
|
|
// 编辑模式:回显数据
|
|
|
|
|
|
form.value = {
|
2026-02-02 16:32:33 +08:00
|
|
|
|
schoolId: row.schoolId,
|
2026-02-02 17:31:27 +08:00
|
|
|
|
schoolCode: row.schoolCode || '',
|
2026-02-02 16:04:47 +08:00
|
|
|
|
schoolName: row.schoolName,
|
|
|
|
|
|
schoolType: row.schoolType,
|
2026-02-03 21:45:14 +08:00
|
|
|
|
deptId: row.deptId,
|
2026-02-02 16:04:47 +08:00
|
|
|
|
regionId: row.regionId,
|
|
|
|
|
|
regionIds: getRegionIdPath(row.regionId),
|
|
|
|
|
|
regionName: row.regionName,
|
|
|
|
|
|
status: row.status
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2026-02-02 17:41:22 +08:00
|
|
|
|
// 新增模式:重置表单,如果有默认区域则带入
|
|
|
|
|
|
const regionIds = defaultRegionId ? getRegionIdPath(defaultRegionId) : []
|
2026-02-02 16:04:47 +08:00
|
|
|
|
form.value = {
|
2026-02-02 16:32:33 +08:00
|
|
|
|
schoolId: null,
|
2026-02-02 17:31:27 +08:00
|
|
|
|
schoolCode: '',
|
2026-02-02 16:04:47 +08:00
|
|
|
|
schoolName: '',
|
|
|
|
|
|
schoolType: '',
|
2026-02-03 21:45:14 +08:00
|
|
|
|
deptId: null,
|
2026-02-02 17:41:22 +08:00
|
|
|
|
regionId: defaultRegionId,
|
|
|
|
|
|
regionIds: regionIds,
|
|
|
|
|
|
regionName: defaultRegionId ? getRegionPath(regionIds, props.regionTree) : '',
|
2026-02-02 16:04:47 +08:00
|
|
|
|
status: '0'
|
|
|
|
|
|
}
|
2026-02-05 11:21:58 +08:00
|
|
|
|
// 重置年级选择
|
|
|
|
|
|
gradeList.value = []
|
|
|
|
|
|
selectedGradeIds.value = []
|
2026-02-02 16:04:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 根据区域ID获取完整的ID路径(用于级联选择器回显)
|
|
|
|
|
|
const getRegionIdPath = (regionId) => {
|
|
|
|
|
|
if (!regionId) return []
|
|
|
|
|
|
|
2026-02-02 17:54:24 +08:00
|
|
|
|
// 递归遍历树查找目标节点,返回从根到目标的完整路径
|
|
|
|
|
|
const findPath = (nodes, targetId, path = []) => {
|
|
|
|
|
|
for (const node of nodes) {
|
|
|
|
|
|
const currentPath = [...path, node.regionId]
|
|
|
|
|
|
if (node.regionId === targetId) {
|
|
|
|
|
|
return currentPath
|
|
|
|
|
|
}
|
|
|
|
|
|
if (node.children && node.children.length > 0) {
|
|
|
|
|
|
const result = findPath(node.children, targetId, currentPath)
|
|
|
|
|
|
if (result.length > 0) {
|
|
|
|
|
|
return result
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return []
|
2026-02-02 16:04:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-02 17:54:24 +08:00
|
|
|
|
return findPath(props.regionTree, regionId)
|
2026-02-02 16:04:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 提交表单
|
|
|
|
|
|
const handleSubmit = async () => {
|
|
|
|
|
|
if (!formRef.value) return
|
|
|
|
|
|
|
|
|
|
|
|
await formRef.value.validate(async (valid) => {
|
|
|
|
|
|
if (!valid) return
|
|
|
|
|
|
|
|
|
|
|
|
submitLoading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
const submitData = {
|
|
|
|
|
|
...form.value,
|
2026-02-05 11:21:58 +08:00
|
|
|
|
regionIds: undefined, // 不提交级联选择器的数组
|
|
|
|
|
|
gradeIds: isEdit.value ? undefined : selectedGradeIds.value // 新增时携带年级ID列表
|
2026-02-02 16:04:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-02 16:32:33 +08:00
|
|
|
|
const res = form.value.schoolId
|
2026-02-02 16:04:47 +08:00
|
|
|
|
? await updateSchool(submitData)
|
|
|
|
|
|
: await addSchool(submitData)
|
|
|
|
|
|
|
|
|
|
|
|
if (res.code === 200) {
|
2026-02-05 11:21:58 +08:00
|
|
|
|
const gradeCount = selectedGradeIds.value.length
|
|
|
|
|
|
const msg = isEdit.value
|
|
|
|
|
|
? '修改成功'
|
|
|
|
|
|
: (gradeCount > 0 ? `新增成功,已自动添加 ${gradeCount} 个年级` : '新增成功')
|
|
|
|
|
|
ElMessage.success(msg)
|
2026-02-02 16:04:47 +08:00
|
|
|
|
dialogVisible.value = false
|
|
|
|
|
|
emit('success')
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('提交失败:', error)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
submitLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 暴露方法给父组件
|
|
|
|
|
|
defineExpose({ open })
|
|
|
|
|
|
</script>
|
2026-02-05 11:21:58 +08:00
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.grade-select-wrapper {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
.grade-select-wrapper .el-checkbox {
|
|
|
|
|
|
margin-right: 16px;
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.grade-tip {
|
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|