feat(B1): 校级建站入口-新建院系子站(向导锁定本校主站)+学校信息服务端继承
- SiteManage: 新建按钮校级显示"新建院系子站";编辑弹窗校级隐藏schId/schName(payload留空服务端继承) - SiteWizard: lockParentId 锁定上级为本校主站禁改 - SchoolWorkbench: 副文案改自助创建口径 - vitest 24/24 绿 + build 成功
This commit is contained in:
@@ -198,7 +198,7 @@ onMounted(run);
|
|||||||
<div class="wb-body">
|
<div class="wb-body">
|
||||||
<div class="wb-title">
|
<div class="wb-title">
|
||||||
<h3>我的学校</h3>
|
<h3>我的学校</h3>
|
||||||
<p class="wb-sub">选择学校进入内容管理;学校与站点由平台管理员在「添加学校」中绑定创建。</p>
|
<p class="wb-sub">选择学校进入内容管理;学校由平台管理员接入,院系子站可在「站点管理」自助创建。</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Spin v-if="loading" size="large" fix />
|
<Spin v-if="loading" size="large" fix />
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ const router = useRouter();
|
|||||||
/** 超管全局视图(工作台入口带入 scope=all) */
|
/** 超管全局视图(工作台入口带入 scope=all) */
|
||||||
const scopeAll = computed(() => route.query.scope === "all" && store.isSuperAdmin);
|
const scopeAll = computed(() => route.query.scope === "all" && store.isSuperAdmin);
|
||||||
|
|
||||||
|
/** B1 建站权限拆分:校级(学校上下文内)可自助创建院系子站;超管向导行为不变 */
|
||||||
|
const isSchoolAdmin = computed(() => !store.isSuperAdmin && !!store.currentSchool);
|
||||||
|
const canCreateSite = computed(() => store.isSuperAdmin || isSchoolAdmin.value);
|
||||||
|
|
||||||
const rows = ref<SiteVo[]>([]);
|
const rows = ref<SiteVo[]>([]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const tableHeight = Utils.useTableHeight({ isOperat: true });
|
const tableHeight = Utils.useTableHeight({ isOperat: true });
|
||||||
@@ -214,8 +218,9 @@ async function save() {
|
|||||||
id: form.id,
|
id: form.id,
|
||||||
code: form.code,
|
code: form.code,
|
||||||
parentSiteId: form.parentSiteId || 0,
|
parentSiteId: form.parentSiteId || 0,
|
||||||
schId: form.schId,
|
// B1:校级角色隐藏学校信息字段,留空由服务端继承主站(防伪造通道)
|
||||||
schName: form.schName || undefined,
|
schId: isSchoolAdmin.value ? undefined : form.schId,
|
||||||
|
schName: isSchoolAdmin.value ? undefined : form.schName || undefined,
|
||||||
name: form.name,
|
name: form.name,
|
||||||
theme: form.theme || undefined,
|
theme: form.theme || undefined,
|
||||||
configJson: t ? mergeThemeParamsIntoConfigJson(form.configJson || "", t, themeParams) : form.configJson || undefined,
|
configJson: t ? mergeThemeParamsIntoConfigJson(form.configJson || "", t, themeParams) : form.configJson || undefined,
|
||||||
@@ -285,10 +290,10 @@ watch(() => store.currentSchool?.schId, () => run());
|
|||||||
<a href="javascript:void(0);" @click="goBackWorkbench">返回学校工作台</a>
|
<a href="javascript:void(0);" @click="goBackWorkbench">返回学校工作台</a>
|
||||||
</Alert>
|
</Alert>
|
||||||
</div>
|
</div>
|
||||||
<div class="fix">
|
<div class="fix" v-if="canCreateSite">
|
||||||
<Button class="fl" type="primary" size="large" @click="openWizard">
|
<Button class="fl" type="primary" size="large" @click="openWizard">
|
||||||
<Icon type="ios-add" />
|
<Icon type="ios-add" />
|
||||||
新建站点(向导)
|
{{ isSchoolAdmin ? "新建院系子站" : "新建站点(向导)" }}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Table :columns="columns" :data="rows" :height="tableHeight" :loading="loading" no-data-text="暂无站点" />
|
<Table :columns="columns" :data="rows" :height="tableHeight" :loading="loading" no-data-text="暂无站点" />
|
||||||
@@ -303,15 +308,15 @@ watch(() => store.currentSchool?.schId, () => run());
|
|||||||
<Input v-model="form.name" :maxlength="128" placeholder="如 XX学校门户网站" />
|
<Input v-model="form.name" :maxlength="128" placeholder="如 XX学校门户网站" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="上级站点">
|
<FormItem label="上级站点">
|
||||||
<Select v-model="form.parentSiteId" clearable placeholder="空=顶层校站点;选主站则为院系子站" style="width: 320px">
|
<Select v-model="form.parentSiteId" :clearable="!isSchoolAdmin" placeholder="空=顶层校站点;选主站则为院系子站" style="width: 320px">
|
||||||
<Option v-for="s in parentOptions" :key="s.id" :value="s.id">{{ s.name }}({{ s.code }})</Option>
|
<Option v-for="s in parentOptions" :key="s.id" :value="s.id">{{ s.name }}({{ s.code }})</Option>
|
||||||
</Select>
|
</Select>
|
||||||
<p class="form-tip">院系子站挂在主站下,门户 header 右上出现"专业院系"下拉;层级固定两层。</p>
|
<p class="form-tip">院系子站挂在主站下,门户 header 右上出现"专业院系"下拉;层级固定两层。{{ isSchoolAdmin ? "校级角色仅可在本校主站之间改挂。" : "" }}</p>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="学校ID">
|
<FormItem v-if="store.isSuperAdmin" label="学校ID">
|
||||||
<InputNumber v-model="form.schId" :min="1" placeholder="UserBase sys_group.id" style="width: 200px" />
|
<InputNumber v-model="form.schId" :min="1" placeholder="UserBase sys_group.id" style="width: 200px" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="学校名称">
|
<FormItem v-if="store.isSuperAdmin" label="学校名称">
|
||||||
<Input v-model="form.schName" :maxlength="128" />
|
<Input v-model="form.schName" :maxlength="128" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="皮肤包">
|
<FormItem label="皮肤包">
|
||||||
@@ -340,8 +345,8 @@ watch(() => store.currentSchool?.schId, () => run());
|
|||||||
<ThemePreviewFrame v-if="previewVisible && editTheme" :site-code="form.code" :theme="form.theme" :theme-params="editThemeParams" />
|
<ThemePreviewFrame v-if="previewVisible && editTheme" :site-code="form.code" :theme="form.theme" :theme-params="editThemeParams" />
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<!-- 新建站点三步向导 -->
|
<!-- 新建站点三步向导(B1:校级锁定为本校主站下建院系子站) -->
|
||||||
<SiteWizard v-model:visible="wizardVisible" :sites="rows" @created="onWizardCreated" />
|
<SiteWizard v-model:visible="wizardVisible" :sites="rows" :lock-parent-id="isSchoolAdmin ? store.currentSchool?.mainSiteId : undefined" @created="onWizardCreated" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import SiteConfigForm from "@/views/cms/components/SiteConfigForm.vue";
|
|||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
sites: SiteVo[];
|
sites: SiteVo[];
|
||||||
|
/** B1 建站权限拆分:校级锁定在本校主站下建院系子站(上级禁改、学校信息由服务端继承) */
|
||||||
|
lockParentId?: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@@ -51,7 +53,12 @@ const canPreview = computed(() => !!sourceSiteCode.value && !!themeName.value);
|
|||||||
const previewTick = ref(0);
|
const previewTick = ref(0);
|
||||||
const previewParams = ref<FormModelValue>({});
|
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() {
|
function debouncePreview() {
|
||||||
previewParams.value = { ...themeParams.value };
|
previewParams.value = { ...themeParams.value };
|
||||||
@@ -71,7 +78,7 @@ watch(
|
|||||||
async v => {
|
async v => {
|
||||||
if (!v) return;
|
if (!v) return;
|
||||||
step.value = 1;
|
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 || "";
|
sourceSiteCode.value = sourceSites.value[0]?.code || "";
|
||||||
if (!themes.value.length) await loadThemes();
|
if (!themes.value.length) await loadThemes();
|
||||||
themeName.value = "default";
|
themeName.value = "default";
|
||||||
@@ -128,8 +135,9 @@ async function createSite() {
|
|||||||
.save({
|
.save({
|
||||||
code: form.code,
|
code: form.code,
|
||||||
parentSiteId: form.parentSiteId || 0,
|
parentSiteId: form.parentSiteId || 0,
|
||||||
schId: form.schId,
|
// B1:校级锁定模式学校信息留空,服务端校验后继承主站(防伪造)
|
||||||
schName: form.schName || undefined,
|
schId: props.lockParentId ? undefined : form.schId,
|
||||||
|
schName: props.lockParentId ? undefined : form.schName || undefined,
|
||||||
name: form.name,
|
name: form.name,
|
||||||
theme: themeName.value,
|
theme: themeName.value,
|
||||||
configJson,
|
configJson,
|
||||||
@@ -148,7 +156,7 @@ async function createSite() {
|
|||||||
<template>
|
<template>
|
||||||
<Modal
|
<Modal
|
||||||
:model-value="visible"
|
:model-value="visible"
|
||||||
:title="`新建站点向导 · 第 ${step} / 3 步`"
|
:title="`${props.lockParentId ? '新建院系子站' : '新建站点'}向导 · 第 ${step} / 3 步`"
|
||||||
:mask-closable="false"
|
:mask-closable="false"
|
||||||
width="980px"
|
width="980px"
|
||||||
@update:model-value="emit('update:visible', $event)"
|
@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" />
|
<Input v-model="form.name" :maxlength="128" placeholder="如 XX学校门户网站" style="width: 320px" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="上级站点">
|
<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>
|
<Option v-for="s in parentOptions" :key="s.id" :value="s.id">{{ s.name }}({{ s.code }})</Option>
|
||||||
</Select>
|
</Select>
|
||||||
|
<div v-if="props.lockParentId" class="pbu-tip">校级角色仅可在本校主站下创建院系子站,学校信息自动继承主站。</div>
|
||||||
</FormItem>
|
</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" />
|
<InputNumber v-model="form.schId" :min="1" placeholder="UserBase sys_group.id" style="width: 200px" />
|
||||||
<!-- B-8:顶层站 schId 必填(校级角色按此匹配管理权限);院系子站留空自动继承主站 -->
|
<!-- B-8:顶层站 schId 必填(校级角色按此匹配管理权限);院系子站留空自动继承主站 -->
|
||||||
<div class="pbu-tip">
|
<div class="pbu-tip">
|
||||||
顶层站点必填(否则建成即校级角色不可管理);选了上级站点的院系子站可留空,自动继承主站。
|
顶层站点必填(否则建成即校级角色不可管理);选了上级站点的院系子站可留空,自动继承主站。
|
||||||
</div>
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="学校名称">
|
<FormItem v-if="!props.lockParentId" label="学校名称">
|
||||||
<Input v-model="form.schName" :maxlength="128" style="width: 320px" />
|
<Input v-model="form.schName" :maxlength="128" style="width: 320px" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="状态">
|
<FormItem label="状态">
|
||||||
|
|||||||
Reference in New Issue
Block a user