You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dianshang-qianduan/src/views/platformAdmin/school/index.vue

317 lines
12 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="school-admin-workbench">
<section class="admin-hero">
<div class="admin-badge">Platform Admin / Schools</div>
<h1 class="admin-title">学校管理</h1>
<p class="admin-subtitle">平台超管维护学校基础档案学校管理员账号和本校业务数据会绑定到这里</p>
</section>
<section class="admin-panel">
<div class="toolbar">
<el-input v-model="query.schoolName" clearable placeholder="学校名称" class="filter-input" @keyup.enter="load" />
<el-button type="primary" @click="load">查询</el-button>
<el-button @click="reset">重置</el-button>
<el-button type="success" @click="openCreate">新增</el-button>
</div>
<el-table v-loading="loading" :data="rows" border>
<el-table-column label="学校名称" prop="schoolName" min-width="180" />
<el-table-column label="学校层次" prop="schoolGrade" min-width="120" />
<el-table-column label="省份" prop="schoolProvince" width="110" />
<el-table-column label="城市" prop="schoolCity" width="110" />
<el-table-column label="联系人" prop="schoolContacts" min-width="120" />
<el-table-column label="联系电话" prop="schoolContactsMobile" min-width="140" />
<el-table-column label="联系邮箱" prop="schoolContactsEmail" min-width="180" />
<el-table-column label="操作" width="160" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="openEdit(row)">编辑</el-button>
<el-button link type="danger" @click="remove(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
class="pager"
layout="total, prev, pager, next, sizes"
:total="total"
v-model:current-page="query.index"
v-model:page-size="query.size"
@current-change="load"
@size-change="handleSizeChange"
/>
<el-dialog
v-model="dialogVisible"
:title="editingId ? '编辑学校' : '新增学校'"
width="680px"
align-center
append-to-body
custom-class="admin-edit-dialog platform-admin-dialog platform-school-dialog"
>
<el-form :model="form" label-width="98px">
<el-form-item label="学校名称">
<el-input v-model="form.schoolName" />
</el-form-item>
<el-form-item label="学校层次">
<el-input v-model="form.schoolGrade" placeholder="如:本科、专科、双一流" />
</el-form-item>
<el-form-item label="省份/城市">
<div class="form-inline">
<el-select v-model="form.schoolProvince" filterable placeholder="请选择省份" @change="handleProvinceChange">
<el-option v-for="item in chinaRegions" :key="item.province" :label="item.province" :value="item.province" />
</el-select>
<el-select v-model="form.schoolCity" filterable placeholder="请选择城市" :disabled="!form.schoolProvince">
<el-option v-for="city in cityOptions" :key="city" :label="city" :value="city" />
</el-select>
</div>
</el-form-item>
<el-form-item label="联系人">
<el-input v-model="form.schoolContacts" />
</el-form-item>
<el-form-item label="联系电话">
<el-input v-model="form.schoolContactsMobile" />
</el-form-item>
<el-form-item label="联系邮箱">
<el-input v-model="form.schoolContactsEmail" />
</el-form-item>
<el-form-item label="学校Logo">
<ImageUpload v-model="form.schoolLogo" class="school-logo-upload" :limit="1" :file-size="2" />
</el-form-item>
<el-form-item label="组织模式">
<el-select v-model="productConfig.organizationMode" :disabled="!editingId" style="width: 100%" @change="applyOrganizationPreset">
<el-option label="标准学校:行政班 + 教学班" value="ADMIN_CLASS_ENABLED" />
<el-option label="教师教学班维护名单" value="TEACHING_CLASS_ONLY" />
<el-option label="自定义配置(由下方开关形成)" value="CUSTOM" disabled />
</el-select>
<div class="config-hint">选择预设会同步更新下方开关;已有组织数据会保留,关闭开关仅隐藏菜单和阻止新增编辑。</div>
</el-form-item>
<el-form-item label="院系管理">
<el-switch v-model="productConfig.facultyManagementEnabled" :disabled="!editingId" @change="handleFacultyManagementChange" />
<span class="config-hint">关闭后会同时关闭专业管理和行政班管理。</span>
</el-form-item>
<el-form-item label="专业管理">
<el-switch v-model="productConfig.majorManagementEnabled" :disabled="!editingId || !productConfig.facultyManagementEnabled" @change="handleMajorManagementChange" />
<span class="config-hint">需先启用院系管理;关闭后会同时关闭行政班管理。</span>
</el-form-item>
<el-form-item label="行政班管理">
<el-switch v-model="productConfig.adminClassManagementEnabled" :disabled="!editingId || !productConfig.facultyManagementEnabled || !productConfig.majorManagementEnabled" @change="syncOrganizationModeFromSwitches" />
<span class="config-hint">关闭后班级管理仅保留教学班,教师维护学生名单。</span>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submit">保存</el-button>
</template>
</el-dialog>
</section>
</div>
</template>
<script setup>
import "@/views/schoolAdmin/admin-theme.scss";
import { ElMessage, ElMessageBox } from "element-plus";
import { addPlatformSchool, deletePlatformSchool, getPlatformSchools, updatePlatformSchool } from "@/api/platformAdmin";
import { getPlatformSchoolProductConfig, updatePlatformSchoolProductConfig } from "@/api/schoolProductConfig";
import { chinaRegions } from "@/utils/chinaRegions";
const query = reactive({ index: 1, size: 10, schoolName: "" });
const rows = ref([]);
const total = ref(0);
const loading = ref(false);
const dialogVisible = ref(false);
const editingId = ref("");
const form = ref({});
const defaultProductConfig = () => ({
organizationMode: "ADMIN_CLASS_ENABLED",
studentRosterOwner: "SCHOOL_ADMIN",
facultyManagementEnabled: true,
majorManagementEnabled: true,
adminClassManagementEnabled: true,
});
const productConfig = ref(defaultProductConfig());
const cityOptions = computed(() => chinaRegions.find((item) => item.province === form.value.schoolProvince)?.cities || []);
const load = async (options = {}) => {
const { showLoading = true } = options;
if (showLoading) {
loading.value = true;
}
try {
const res = await getPlatformSchools(query);
rows.value = res.data?.list || [];
total.value = res.data?.total || 0;
} finally {
if (showLoading) {
loading.value = false;
}
}
};
const reset = () => {
query.index = 1;
query.schoolName = "";
load();
};
const handleSizeChange = () => {
query.index = 1;
load();
};
const openCreate = () => {
editingId.value = "";
form.value = {
schoolName: "",
schoolGrade: "",
schoolProvince: "",
schoolCity: "",
schoolContacts: "",
schoolContactsMobile: "",
schoolContactsEmail: "",
schoolLogo: "",
};
productConfig.value = defaultProductConfig();
dialogVisible.value = true;
};
const openEdit = async (row) => {
editingId.value = row.schoolId;
form.value = { ...row };
const res = await getPlatformSchoolProductConfig(row.schoolId);
productConfig.value = normalizeProductConfig(res.data);
dialogVisible.value = true;
};
const handleProvinceChange = () => {
form.value.schoolCity = "";
};
const normalizeProductConfig = (config) => {
const current = config || defaultProductConfig();
const legacyAdminEnabled = current.organizationMode !== "TEACHING_CLASS_ONLY";
return {
...current,
facultyManagementEnabled: current.facultyManagementEnabled ?? legacyAdminEnabled,
majorManagementEnabled: current.majorManagementEnabled ?? legacyAdminEnabled,
adminClassManagementEnabled: current.adminClassManagementEnabled ?? legacyAdminEnabled,
};
};
const applyOrganizationPreset = (organizationMode) => {
if (organizationMode === "ADMIN_CLASS_ENABLED") {
productConfig.value = { ...productConfig.value, ...defaultProductConfig() };
return;
}
if (organizationMode === "TEACHING_CLASS_ONLY") {
productConfig.value = {
...productConfig.value,
organizationMode: "TEACHING_CLASS_ONLY",
studentRosterOwner: "TEACHER",
facultyManagementEnabled: false,
majorManagementEnabled: false,
adminClassManagementEnabled: false,
};
}
};
const handleFacultyManagementChange = (enabled) => {
if (!enabled) {
productConfig.value.majorManagementEnabled = false;
productConfig.value.adminClassManagementEnabled = false;
}
syncOrganizationModeFromSwitches();
};
const handleMajorManagementChange = (enabled) => {
if (!enabled) {
productConfig.value.adminClassManagementEnabled = false;
}
syncOrganizationModeFromSwitches();
};
const syncOrganizationModeFromSwitches = () => {
const { facultyManagementEnabled, majorManagementEnabled, adminClassManagementEnabled } = productConfig.value;
if (facultyManagementEnabled && majorManagementEnabled && adminClassManagementEnabled) {
productConfig.value.organizationMode = "ADMIN_CLASS_ENABLED";
productConfig.value.studentRosterOwner = "SCHOOL_ADMIN";
} else if (!facultyManagementEnabled && !majorManagementEnabled && !adminClassManagementEnabled) {
productConfig.value.organizationMode = "TEACHING_CLASS_ONLY";
productConfig.value.studentRosterOwner = "TEACHER";
} else {
productConfig.value.organizationMode = "CUSTOM";
productConfig.value.studentRosterOwner = "TEACHER";
}
};
const buildSchoolPayload = () => ({
schoolName: form.value.schoolName,
schoolGrade: form.value.schoolGrade,
schoolProvince: form.value.schoolProvince,
schoolCity: form.value.schoolCity,
schoolContacts: form.value.schoolContacts,
schoolContactsMobile: form.value.schoolContactsMobile,
schoolContactsEmail: form.value.schoolContactsEmail,
schoolLogo: form.value.schoolLogo,
});
const submit = async () => {
const payload = buildSchoolPayload();
if (editingId.value) {
await updatePlatformSchoolProductConfig(editingId.value, {
organizationMode: productConfig.value.organizationMode,
studentRosterOwner: productConfig.value.studentRosterOwner,
facultyManagementEnabled: productConfig.value.facultyManagementEnabled,
majorManagementEnabled: productConfig.value.majorManagementEnabled,
adminClassManagementEnabled: productConfig.value.adminClassManagementEnabled,
});
await updatePlatformSchool(editingId.value, payload);
} else {
await addPlatformSchool(payload);
}
ElMessage.success("保存成功");
dialogVisible.value = false;
load({ showLoading: false });
};
const remove = async (row) => {
await ElMessageBox.confirm("确认删除该学校?学校下存在业务数据时后端会拒绝删除。", "提示", { type: "warning" });
await deletePlatformSchool(row.schoolId);
ElMessage.success("删除成功");
load({ showLoading: false });
};
onMounted(load);
</script>
<style scoped lang="scss">
.form-inline {
display: grid;
width: 100%;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
}
.school-logo-upload {
width: 100%;
}
.school-logo-upload :deep(.el-upload-list--picture-card .el-upload-list__item),
.school-logo-upload :deep(.el-upload--picture-card) {
width: 96px;
height: 96px;
}
.config-hint {
color: #7a8499;
font-size: 12px;
line-height: 1.6;
margin-top: 6px;
}
@media (max-width: 640px) {
.form-inline {
grid-template-columns: 1fr;
}
}
</style>