feat(theme): 皮肤包管理收敛平台级+命名表单-新顶层路由/theme-manage(meta.superOnly守卫+页面watch双兜底),ThemeManage升级全屏页复用新抽WorkbenchHeader(工作台同款深色顶栏backTo=/workbench),工作台超管工具行加皮肤包管理按钮,删SiteManage兜底按钮,旧/theme重定向新页;上传/添加皮肤包改表单弹窗(zip+显示名/描述;添加=源包+标识名+命名一步),内置包可改显示名/描述(无参数区+恢复默认名称);openEdit未注册主题保留原值并告警(themeEdit util,不再静默归一default);mock删modId=7菜单节点;SiteWizard预览内容源v-model对齐path语义(W4遗留);vitest 62/62+build通过
build / build (push) Successful in 2m17s

This commit is contained in:
chenxing
2026-09-08 20:25:57 +08:00
parent 604d77049b
commit 9ba4cb135a
12 changed files with 553 additions and 218 deletions
+15 -11
View File
@@ -9,6 +9,7 @@ import { useCmsStore } from "@/store";
import config from "@/assets/config/host.config";
import { SiteVo, ThemeVo } from "@/types/cms";
import { formModelFromTheme, FormModelValue, mergeThemeParamsIntoConfigJson, themeParamsFromConfigJson } from "@/utils/themeParamForm";
import { resolveEditableTheme } from "@/utils/themeEdit";
import ThemePicker from "@/components/theme/ThemePicker.vue";
import ThemeParamForm from "@/components/theme/ThemeParamForm.vue";
import ThemePreviewFrame from "@/components/theme/ThemePreviewFrame.vue";
@@ -182,17 +183,18 @@ function openWizard() {
wizardVisible.value = true;
}
/** W1:皮肤包管理页入口 */
function goThemeManage() {
router.push(`/sch/${store.schoolCode}/theme`);
}
function onWizardCreated() {
run();
}
function openEdit(row: SiteVo) {
editSitePath.value = row.path || row.code;
// 任务三:主题值保留原值(未注册时告警,由服务端校验拒绝),不再静默改写为 default
const resolved = resolveEditableTheme(
row.theme,
themes.value.map(t => t.name)
);
themeUnregistered.value = resolved.unregistered;
Object.assign(form, {
id: row.id,
code: row.code,
@@ -200,7 +202,7 @@ function openEdit(row: SiteVo) {
schId: row.schId,
schName: row.schName || "",
name: row.name,
theme: themeMeta.value[row.theme || "default"] ? row.theme! : "default",
theme: resolved.theme,
configJson: row.configJson || "",
status: row.status,
});
@@ -236,6 +238,7 @@ function withCustomCss(configJson: string, css: string): string {
function pickTheme(name: string) {
if (form.theme === name) return;
form.theme = name;
themeUnregistered.value = false;
const t = editTheme.value;
editThemeParams.value = t ? formModelFromTheme(t, editThemeParams.value) : {};
}
@@ -332,11 +335,6 @@ watch(() => store.currentSchool?.schId, () => run());
<Icon type="ios-add" />
{{ isSchoolAdmin ? "新建院系子站" : "新建站点(向导)" }}
</Button>
<!-- W1:皮肤包管理入口(平台菜单注册前兜底可达,方案 §5.4 步骤7/R3) -->
<Button v-if="isSuperAdmin" class="fl" size="large" style="margin-left: 12px" @click="goThemeManage">
<Icon type="ios-color-palette" />
皮肤包管理
</Button>
</div>
<Table :columns="columns" :data="rows" :height="tableHeight" :loading="loading" no-data-text="暂无站点" />
@@ -362,6 +360,12 @@ watch(() => store.currentSchool?.schId, () => run());
<Input v-model="form.schName" :maxlength="128" />
</FormItem>
<FormItem label="皮肤包">
<Alert v-if="themeUnregistered" type="warning" show-icon style="margin-bottom: 8px">
皮肤包「{{ form.theme }}」未在注册表中,保存将被服务端拒绝;请先由平台超管上传该皮肤包,或改选下方其他皮肤。
</Alert>
<Alert v-else-if="!themes.length" type="warning" show-icon style="margin-bottom: 8px">
皮肤包列表加载失败,暂时无法选择;站点当前皮肤包({{ form.theme }})保持不变,可关闭后重试。
</Alert>
<ThemePicker :themes="themes" :model-value="form.theme" @update:model-value="pickTheme" />
<Button size="small" class="preview-btn" :disabled="!editTheme" @click="openEditPreview">预览本皮肤</Button>
<ThemeParamForm :theme="editTheme" v-model="editThemeParams" />