feat(theme): S5 学院蓝主题改造(管理端)——homeLayout编辑器/主题参数表单/站点设置/类型 + 单测
build / build (push) Successful in 2m19s

This commit is contained in:
chenxing
2026-09-22 18:34:50 +08:00
parent abccca4397
commit d05272b820
14 changed files with 893 additions and 186 deletions
+7 -18
View File
@@ -7,10 +7,8 @@ import config from "@/assets/config/host.config";
import { portalUrlOf, adminPathOf, previewPortalUrl } from "@/utils/sitePath";
import { useCmsStore } from "@/store";
import { SiteVo, ThemeVo } from "@/types/cms";
import { FormModelValue, formModelFromTheme, 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";
import SiteConfigForm from "@/views/cms/components/SiteConfigForm.vue";
import HomeLayoutEditor from "@/views/cms/homeLayout/HomeLayoutEditor.vue";
@@ -46,7 +44,7 @@ const themes = ref<ThemeVo[]>([]);
/** 分区清单(锚点导航;locked = 创建模式锁定) */
const sections = [
{ key: "basic", title: "基本信息", desc: "编码 / 名称 / 归属 / 状态" },
{ key: "theme", title: "皮肤与外观", desc: "风格选择与微调" },
{ key: "theme", title: "皮肤与外观", desc: "选皮肤 / 自定义 CSS" },
{ key: "layout", title: "首页布局", desc: "区块序列编排", locked: true },
{ key: "display", title: "基础展示", desc: "品牌标识与图片" },
{ key: "footer", title: "页脚信息", desc: "联系方式与社媒" },
@@ -71,10 +69,9 @@ const basic = reactive({
status: 1,
});
/** ② 皮肤与外观分区 */
/** ② 皮肤与外观分区(工作项 T:参数微调已迁皮肤包管理,站点只负责选哪套包) */
const theme = ref("default");
const themeUnregistered = ref(false);
const themeParams = ref<FormModelValue>({});
const customCss = ref("");
const previewVisible = ref(false);
const editTheme = computed(() => themes.value.find(t => t.name === theme.value));
@@ -187,8 +184,6 @@ function initFromRow(id: number) {
theme.value = resolved.theme;
configJson.value = r.configJson || "";
customCss.value = readCustomCss(configJson.value);
const t = editTheme.value;
themeParams.value = t ? themeParamsFromConfigJson(configJson.value, t) : {};
}
function readCustomCss(configJson: string): string {
@@ -218,8 +213,6 @@ function pickTheme(name: string) {
if (theme.value === name) return;
theme.value = name;
themeUnregistered.value = false;
const t = editTheme.value;
themeParams.value = t ? formModelFromTheme(t, themeParams.value) : {};
}
function openPreview() {
@@ -228,7 +221,7 @@ function openPreview() {
/**
* 统一提交(§3.5 分区独立保存模型):payload = 页面当前事实源 + 基本信息快照;
* patch.configJson 供分区保存前合并自己的键空间(如皮肤分区的 theme_params/custom_css)。
* patch.configJson 供分区保存前合并自己的键空间(如皮肤分区的 custom_css)。
* 创建成功原地 replace 到 site/setting/{id} 并解锁全部分区。
*/
async function submitSection(key: string, patch: { configJson?: string }) {
@@ -259,7 +252,7 @@ async function submitSection(key: string, patch: { configJson?: string }) {
Message.success("保存成功!");
if (res) {
row.value = res;
// 服务端归一后的 configJson 刷新事实源(theme_params 合并等)
// 服务端归一后的 configJson 刷新事实源
if (typeof res.configJson === "string") {
configJson.value = res.configJson;
customCss.value = readCustomCss(configJson.value);
@@ -281,10 +274,7 @@ async function saveBasic() {
}
async function saveTheme() {
const t = editTheme.value;
const baseConfig = withCustomCss(configJson.value, customCss.value);
const merged = t ? mergeThemeParamsIntoConfigJson(baseConfig, t, themeParams.value) : baseConfig;
await submitSection("theme", { configJson: merged });
await submitSection("theme", { configJson: withCustomCss(configJson.value, customCss.value) });
}
async function saveDisplay() {
@@ -466,7 +456,7 @@ watch(() => store.currentSchool?.schId, () => loadSites());
<div id="sec-theme" class="card setting-card" data-section="theme">
<div class="card-header">
<span class="card-title">皮肤与外观</span>
<span class="card-subtitle">风格选择 / 参数微调 / 站点级 CSS 覆盖</span>
<span class="card-subtitle">风格选择 / 站点级 CSS 覆盖(微调请去「皮肤包管理」)</span>
</div>
<div class="card-body">
<div class="field-block">
@@ -479,7 +469,6 @@ watch(() => store.currentSchool?.schId, () => loadSites());
</Alert>
<ThemePicker :themes="themes" :model-value="theme" @update:model-value="pickTheme" />
<Button size="small" class="preview-btn" :disabled="!editTheme" @click="openPreview">预览本皮肤</Button>
<ThemeParamForm :theme="editTheme" v-model="themeParams" />
</div>
<!-- B3 自定义样式(config_json.custom_css,服务端黑名单强校验) -->
<Collapse class="css-collapse">
@@ -594,7 +583,7 @@ watch(() => store.currentSchool?.schId, () => loadSites());
v-if="previewVisible && editTheme && row"
:site-path="row.path || row.code"
:theme="theme"
:theme-params="themeParams"
:theme-params="editTheme?.paramValues || {}"
/>
</Modal>
</div>