feat(cms): docs/16 站点设置整页化+首页布局编辑器- SiteSetting.vue整页7分区(基本信息/皮肤与外观/首页布局/基础展示/页脚信息/SEO与域名/扩展参数)锚点导航sticky+IntersectionObserver+分区卡片三段式每分区独立保存(configJson单一事实源+submitSection统一提交);创建模式仅①②可编辑其余锁定,创建成功原地replace到site/setting/:id;SiteWizard三步向导退役删除(门户预览收编portalPreviewUrl);SiteManage瘦身表格+行内编辑跳整页(adminPathOf子站带站点段);ImageAssetField图片双通道(上传+路径粘贴折叠);SiteConfigForm支持groups/showExtras/siteId+asset字段接ImageAssetField+三旧键表单不再展示(ABSORBED_LEGACY_KEYS静默保留);homeLayout三件套(homeLayout.ts模型parse/serialize/validate/genFromCategories一键生成+blockTypes.ts注册表showIf条件字段+HomeLayoutEditor.vue序列编辑增删显隐上移下移);routes site/new+site/setting/:id+legacy redirect;CategoryManage home_style提示改口指向首页布局;新增siteSetting.spec+homeLayout.spec,重写siteManage.spec,117用例全绿
build / build (push) Successful in 2m19s

This commit is contained in:
chenxing
2026-09-10 15:09:10 +08:00
parent 7c925cd4f2
commit 34733ec280
16 changed files with 2399 additions and 840 deletions
+11 -5
View File
@@ -107,10 +107,9 @@ describe("siteConfigForm", () => {
expect(firstExtraError([])).toBe("");
});
it("已知键清单与 docs/09 §0 键盘点表一致(防漂移)", () => {
it("已知键清单与 docs/09 §0 键盘点表一致(防漂移;docs/16 D4 吸收三旧键后收敛)", () => {
expect(SITE_CONFIG_FIELD_DEFS.map(d => d.key)).toEqual([
"logo", "favicon", "slogan", "colleges_label",
"news_bg_image", "gallery_title", "gallery_subtitle",
"contact", "footer_addresses", "qr_wechat", "qr_weibo",
"social_wechat", "social_weibo", "social_douyin", "social_bilibili",
"copyright", "beian",
@@ -118,13 +117,12 @@ describe("siteConfigForm", () => {
"domain", "public_base_url",
]);
});
it("docs/12:页脚增强与首页视觉新键 parse/serialize/校验(qr_* asset、social_* url)", () => {
it("docs/12:页脚增强新键 parse/serialize/校验(qr_* asset、social_* url)", () => {
const json = JSON.stringify({
footer_addresses: "潭山校区:南湖路 237 号 邮编:464000\n淮河校区:大学路 1 号 邮编:464300",
qr_wechat: "/cms/upload/qr-wechat.png",
social_weibo: "https://weibo.com/xxx",
social_douyin: "not-a-url",
gallery_title: "线上展厅",
});
const m = parseSiteConfig(json);
expect(m.fields.footer_addresses).toContain("淮河校区");
@@ -140,7 +138,15 @@ describe("siteConfigForm", () => {
const out = JSON.parse(serializeSiteConfig(m));
expect(out.footer_addresses).toContain("邮编:464300");
expect(out).not.toHaveProperty("social_bilibili");
expect(out.gallery_title).toBe("线上展厅");
});
it("docs/16 D4:gallery_title/gallery_subtitle/news_bg_image 被吸收为静默保留键(不落扩展行、无损回写)", () => {
const json = JSON.stringify({ gallery_title: "线上展厅", news_bg_image: "/cms/upload/campus.jpg" });
const m = parseSiteConfig(json);
expect(m.fields).toEqual({}); // 不进任何分区表单
expect(m.extras).toEqual([]); // 也不落扩展行
expect(m.preserved.gallery_title).toBe("线上展厅");
expect(m.preserved.news_bg_image).toBe("/cms/upload/campus.jpg");
expect(JSON.parse(serializeSiteConfig(m))).toEqual(JSON.parse(json)); // 存量值无损保留
});
it("B3:custom_css 为保留键,不入扩展行,parse/serialize 无损往返", () => {
const json = JSON.stringify({ custom_css: ".site-header{padding:16px 0}", my_ext: "v" });
+11 -17
View File
@@ -39,21 +39,6 @@ export const SITE_CONFIG_FIELD_DEFS: SiteConfigFieldDef[] = [
placeholder: "留空默认「专业院系」",
tip: "门户右上「专业院系」下拉的文案;仅主站有效(下拉由院系子站生成)。",
},
{
key: "news_bg_image", label: "新闻卡背景图", group: "display", kind: "asset", maxLength: 500,
placeholder: "如 /yschcms/upload/…/campus.jpg(留空用主题渐变)",
tip: "首页「双栏新闻卡」板块的全幅背景大图(建议 1920×700);仅展示型皮肤(campus-glow)消费。",
},
{
key: "gallery_title", label: "展厅标题", group: "display", kind: "text", maxLength: 50,
placeholder: "留空默认「线上展厅」",
tip: "首页图片画廊文区头主标题。",
},
{
key: "gallery_subtitle", label: "展厅英文副题", group: "display", kind: "text", maxLength: 100,
placeholder: "留空默认 Online Exhibition Hall",
tip: "首页图片画廊文区头英文副题。",
},
{
key: "contact", label: "联系方式", group: "footer", kind: "text", maxLength: 200, multiline: true,
placeholder: "如:电话 0571-88888888 · 地址杭州市 XX 路 100 号",
@@ -119,9 +104,18 @@ export const SITE_CONFIG_GROUPS: { name: SiteConfigGroup; title: string }[] = [
{ name: "domain", title: "域名与访问" },
];
/** 系统托管键:不进表单,解析时原样保留、序列化时无损回写(theme_params 由主题参数表单维护) */
/**
* 系统托管键:不进表单,解析时原样保留、序列化时无损回写(theme_params 由主题参数表单维护;
* custom_css 由站点设置「皮肤与外观」分区维护)。
*/
export const RESERVED_SITE_CONFIG_KEYS = ["theme_params", "custom_css"];
/**
* docs/16 D4 被吸收的旧键:画廊标题/副题、新闻卡背景图改由首页布局区块属性承载(home_layout),
* 分区表单不再展示;存量值静默保留(渲染端仍作区块未设 title/background 时的默认值)。
*/
export const ABSORBED_LEGACY_KEYS = ["gallery_title", "gallery_subtitle", "news_bg_image"];
/** 扩展键名约束(与后端 CONFIG_KEY_PATTERN 一致) */
export const EXTRA_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
@@ -160,7 +154,7 @@ export function parseSiteConfig(configJson: string | undefined | null): SiteConf
return model;
}
const known = new Set(SITE_CONFIG_FIELD_DEFS.map(f => f.key));
const reserved = new Set(RESERVED_SITE_CONFIG_KEYS);
const reserved = new Set([...RESERVED_SITE_CONFIG_KEYS, ...ABSORBED_LEGACY_KEYS]);
Object.keys(parsed).forEach(k => {
const v = parsed![k];
if (typeof v !== "string") {