[CMS-REWORK] #1 首页布局编辑器数据栏目为空: 栏目树加载与首用自动编排解耦(挂载+siteId watch双通道幂等加载)

This commit is contained in:
chenxing
2026-09-29 10:49:49 +08:00
parent cff4325947
commit 4208f8ca89
2 changed files with 77 additions and 8 deletions
+53 -2
View File
@@ -49,7 +49,10 @@ const TREE: CategoryTreeNode[] = [
{ id: 12, name: "媒体聚焦", type: 1, children: [] },
] as unknown as CategoryTreeNode[];
async function mountEditor(configJson: string, opts?: { siteId?: number; portalUrl?: string }) {
async function mountEditor(
configJson: string,
opts?: { siteId?: number; portalUrl?: string; siteIdRef?: ReturnType<typeof ref<number | undefined>> },
) {
const el = document.createElement("div");
document.body.appendChild(el);
const model = ref(configJson);
@@ -58,7 +61,7 @@ async function mountEditor(configJson: string, opts?: { siteId?: number; portalU
render: () =>
h(HomeLayoutEditor, {
modelValue: model.value,
siteId: opts?.siteId,
siteId: opts?.siteIdRef ? opts.siteIdRef.value : opts?.siteId,
portalUrl: opts?.portalUrl,
"onUpdate:modelValue": (v: string) => (model.value = v),
}),
@@ -237,6 +240,54 @@ describe("HomeLayoutEditor - F2 三栏 + 预览 + 缩略图 + 自动编排(doc
});
});
describe("HomeLayoutEditor - 栏目树加载与首用编排解耦(docs/20 返工 #1)", () => {
it("已有布局 + 有列表栏目 + siteId:栏目树仍无条件加载,数据栏目可勾选", async () => {
treeMock.send.mockResolvedValue(TREE);
const config = JSON.stringify({
home_layout: { version: 1, blocks: [{ id: "a1", type: "article_list", categoryIds: [11], style: "card", size: 8 }] },
});
const { el, model } = await mountEditor(config, { siteId: 1 });
// 树加载发生(幂等标记下 treeMock 恰好 1 次调用)
expect(treeMock.send).toHaveBeenCalledTimes(1);
(el.querySelector(".hle-row") as HTMLElement).click();
await settle();
const cats = fieldOf(el as HTMLElement, "数据栏目")!;
expect(cats).not.toBeNull();
expect(cats.querySelectorAll(".hle-cat").length).toBe(2);
expect(cats.textContent).toContain("校园时讯");
expect(cats.textContent).toContain("媒体聚焦");
// 已有布局不被自动编排改写
expect(JSON.parse(model.value).home_layout.blocks[0].id).toBe("a1");
});
it("siteId 晚于挂载到位(父级异步种入):watch 补跑加载,数据栏目可用", async () => {
treeMock.send.mockResolvedValue(TREE);
const siteIdRef = ref<number | undefined>(undefined);
const config = JSON.stringify({
home_layout: { version: 1, blocks: [{ id: "a1", type: "article_list", categoryIds: [], style: "card", size: 8 }] },
});
const { el } = await mountEditor(config, { siteIdRef });
await settle();
// siteId 未到位时不请求
expect(treeMock.send).not.toHaveBeenCalled();
siteIdRef.value = 1;
await settle(6);
expect(treeMock.send).toHaveBeenCalledTimes(1);
(el.querySelector(".hle-row") as HTMLElement).click();
await settle();
const cats = fieldOf(el as HTMLElement, "数据栏目")!;
expect(cats.querySelectorAll(".hle-cat").length).toBe(2);
});
it("watch + onMounted 双触发下幂等:同 siteId 只请求一次", async () => {
treeMock.send.mockResolvedValue(TREE);
const siteIdRef = ref<number | undefined>(1);
await mountEditor(JSON.stringify({ logo: "/a.png" }), { siteIdRef });
await settle(6);
expect(treeMock.send).toHaveBeenCalledTimes(1);
});
});
describe("HomeLayoutEditor - D 组件属性面板(docs/20 D-1/D-8)", () => {
it("stats_band:统计项三列编辑器(名称/数值/单位)渲染并可增行", async () => {
const config = JSON.stringify({