feat(B1): 校级建站入口-新建院系子站(向导锁定本校主站)+学校信息服务端继承

- SiteManage: 新建按钮校级显示"新建院系子站";编辑弹窗校级隐藏schId/schName(payload留空服务端继承)
- SiteWizard: lockParentId 锁定上级为本校主站禁改
- SchoolWorkbench: 副文案改自助创建口径
- vitest 24/24 绿 + build 成功
This commit is contained in:
chenxing
2026-09-08 15:14:07 +08:00
parent c77be8b5d8
commit 3990811ed5
3 changed files with 33 additions and 19 deletions
+17 -8
View File
@@ -17,6 +17,8 @@ import SiteConfigForm from "@/views/cms/components/SiteConfigForm.vue";
const props = defineProps<{
visible: boolean;
sites: SiteVo[];
/** B1 建站权限拆分:校级锁定在本校主站下建院系子站(上级禁改、学校信息由服务端继承) */
lockParentId?: number;
}>();
const emit = defineEmits<{
@@ -51,7 +53,12 @@ const canPreview = computed(() => !!sourceSiteCode.value && !!themeName.value);
const previewTick = ref(0);
const previewParams = ref<FormModelValue>({});
const parentOptions = computed(() => props.sites.filter(s => !s.parentSiteId));
/** 锁定模式:上级仅本校主站;否则全部顶层站可选 */
const parentOptions = computed(() =>
props.lockParentId
? props.sites.filter(s => s.id === props.lockParentId && !s.parentSiteId)
: props.sites.filter(s => !s.parentSiteId)
);
function debouncePreview() {
previewParams.value = { ...themeParams.value };
@@ -71,7 +78,7 @@ watch(
async v => {
if (!v) return;
step.value = 1;
Object.assign(form, { code: "", parentSiteId: undefined, schId: undefined, schName: "", name: "", status: 1, configJson: "" });
Object.assign(form, { code: "", parentSiteId: props.lockParentId, schId: undefined, schName: "", name: "", status: 1, configJson: "" });
sourceSiteCode.value = sourceSites.value[0]?.code || "";
if (!themes.value.length) await loadThemes();
themeName.value = "default";
@@ -128,8 +135,9 @@ async function createSite() {
.save({
code: form.code,
parentSiteId: form.parentSiteId || 0,
schId: form.schId,
schName: form.schName || undefined,
// B1:校级锁定模式学校信息留空,服务端校验后继承主站(防伪造)
schId: props.lockParentId ? undefined : form.schId,
schName: props.lockParentId ? undefined : form.schName || undefined,
name: form.name,
theme: themeName.value,
configJson,
@@ -148,7 +156,7 @@ async function createSite() {
<template>
<Modal
:model-value="visible"
:title="`新建站点向导 · 第 ${step} / 3 步`"
:title="`${props.lockParentId ? '新建院系子站' : '新建站点'}向导 · 第 ${step} / 3 步`"
:mask-closable="false"
width="980px"
@update:model-value="emit('update:visible', $event)"
@@ -200,18 +208,19 @@ async function createSite() {
<Input v-model="form.name" :maxlength="128" placeholder="如 XX学校门户网站" style="width: 320px" />
</FormItem>
<FormItem label="上级站点">
<Select v-model="form.parentSiteId" clearable placeholder="空=顶层校站点;选主站则为院系子站" style="width: 320px">
<Select v-model="form.parentSiteId" :disabled="!!props.lockParentId" placeholder="空=顶层校站点;选主站则为院系子站" style="width: 320px">
<Option v-for="s in parentOptions" :key="s.id" :value="s.id">{{ s.name }}({{ s.code }})</Option>
</Select>
<div v-if="props.lockParentId" class="pbu-tip">校级角色仅可在本校主站下创建院系子站,学校信息自动继承主站。</div>
</FormItem>
<FormItem label="学校ID">
<FormItem v-if="!props.lockParentId" label="学校ID">
<InputNumber v-model="form.schId" :min="1" placeholder="UserBase sys_group.id" style="width: 200px" />
<!-- B-8:顶层站 schId 必填(校级角色按此匹配管理权限);院系子站留空自动继承主站 -->
<div class="pbu-tip">
顶层站点必填(否则建成即校级角色不可管理);选了上级站点的院系子站可留空,自动继承主站。
</div>
</FormItem>
<FormItem label="学校名称">
<FormItem v-if="!props.lockParentId" label="学校名称">
<Input v-model="form.schName" :maxlength="128" style="width: 320px" />
</FormItem>
<FormItem label="状态">