- 新建 components/category/CategoryContentSummary.vue(194):列表型栏目右侧不再挂文章表格, 改「共 N 篇 + 最近 5 行 + 查看全部」概览;计数口径=直属+子孙(与门户 collectListDescendantIds 同语义)、全状态计入,一次 page 请求同时拿 total 与 5 行(零新接口、零后端改动) - CategoryManage.vue 列表分支换组件 + 头注更新;编辑弹窗「首页展示形态」提示改为 「展示形态由当前皮肤包的主题化实现决定,未实现的形态回落通用骨架渲染」(v2 §七 验收第 8 条点名项) - ArticleListPanel.vue +7:setup 读 route.query.categoryId 作为初始筛选(深链回显) - types/cms.ts 1 行:ArticlePageQuery.categoryIds 放宽为 number|string(逗号串),手册未点名、已在 audit-D §六 披露为偏差 - categoryWorkbench.spec.ts 15→17 例:T-D-01 按裁决改写旧断言(概览而非表格,并机器证明无操作列)、 T-D-02 深链回显、T-D-03 查看全部跳转;夹具上提 + beforeEach 复位,默认值与旧硬编码行为逐字一致 - 未动 router/routes.ts 的 meta.subOf(护栏 siteRouter.spec 8 / siteLayer.spec 16 绿) - 遗留立案:R-43 fixedCategory/extraHeight 无生产调用方(按纪律未删)/ R-44 概览含子孙而深链只筛直属 需产品定口径 / R-45 菜单名来自平台模块表 / R-46 ?categoryId 仅读一次 - 前端 166/166 绿 + build:base exit=0;S10 复跑同绿
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
// @vitest-environment jsdom
|
||||
/**
|
||||
* B5 栏目工作台组件行为(方案 P1-2):
|
||||
* ① 列表型栏目 → 右侧 ArticleListPanel 且 categoryId 正确传递;
|
||||
* ② 切换树节点 → 面板 watch categoryId 联动 reload;
|
||||
* B5 栏目工作台组件行为(方案 P1-2)+ P-D1 内容管理 IA 切分(v2 §B):
|
||||
* ① 列表型栏目 → 右侧「栏目内容概览」(统计 + 最近 5 条只读 + CTA),不再渲染完整操作表;
|
||||
* ② 切换树节点 → 概览 watch categoryId 联动 reload;
|
||||
* ③ 单页型 → 摘要面板(含"编辑内容"入口)不渲染文章列表;
|
||||
* ④ 外链型 → linkUrl + 打开链接;
|
||||
* ⑤ 新建文章按钮 → 路由跳转带 categoryId query。
|
||||
* ⑤ 新建文章按钮 → 路由跳转带 categoryId query;
|
||||
* ⑥ T-D-02/03:/article 读 ?categoryId= 预筛选;概览"查看全部"深链到该地址。
|
||||
*
|
||||
* 无 @vue/test-utils 依赖:createApp 手写挂载 + vi.mock 桩(树面板/排序弹窗/图标/确认框/api/router)。
|
||||
*/
|
||||
@@ -14,20 +15,26 @@ import { createApp, defineComponent, h, nextTick, reactive } from "vue";
|
||||
import { createPinia } from "pinia";
|
||||
import ViewUIPlus from "view-ui-plus";
|
||||
|
||||
const { pageMock, routerPush, openMock, saveMock, saveRejection } = vi.hoisted(() => ({
|
||||
const { pageMock, routerPush, openMock, saveMock, saveRejection, pageResult, treeFixture, routeMock } = vi.hoisted(() => ({
|
||||
pageMock: vi.fn(),
|
||||
routerPush: vi.fn(),
|
||||
openMock: vi.fn(),
|
||||
saveMock: vi.fn(),
|
||||
/** 第 N 次(0 起)save.send() 拒绝;-1=全部成功(批量添加"失败继续"用) */
|
||||
saveRejection: { value: -1 },
|
||||
/** articleApi.page 返回值桩:默认空列表(各用例按需覆盖 list/total) */
|
||||
pageResult: { value: { list: [] as any[], total: 0 } },
|
||||
/** categoryApi.tree 返回值桩:默认空树(用例按需给栏目层级) */
|
||||
treeFixture: { value: [] as any[] },
|
||||
/** useRoute 桩:可变 query,测 ?categoryId= 深链初始化 */
|
||||
routeMock: { value: { params: {}, query: {} as Record<string, string> } },
|
||||
}));
|
||||
|
||||
vi.mock("@/api/module/cms", () => ({
|
||||
articleApi: {
|
||||
page: (query: any) => {
|
||||
pageMock(query);
|
||||
return { send: () => Promise.resolve({ list: [], total: 0 }) };
|
||||
return { send: () => Promise.resolve(pageResult.value) };
|
||||
},
|
||||
publish: () => ({ send: () => Promise.resolve({}) }),
|
||||
offline: () => ({ send: () => Promise.resolve({}) }),
|
||||
@@ -35,7 +42,7 @@ vi.mock("@/api/module/cms", () => ({
|
||||
delete: () => ({ send: () => Promise.resolve({}) }),
|
||||
},
|
||||
categoryApi: {
|
||||
tree: () => ({ send: () => Promise.resolve([]) }),
|
||||
tree: () => ({ send: () => Promise.resolve(treeFixture.value) }),
|
||||
save: (payload: any) => {
|
||||
const nth = saveMock.mock.calls.length;
|
||||
saveMock(payload);
|
||||
@@ -54,7 +61,7 @@ vi.mock("@/api/module/cms", () => ({
|
||||
|
||||
vi.mock("vue-router", () => ({
|
||||
useRouter: () => ({ push: routerPush, replace: vi.fn() }),
|
||||
useRoute: () => ({ params: {}, query: {} }),
|
||||
useRoute: () => routeMock.value,
|
||||
}));
|
||||
|
||||
/** 树面板桩:把 emit 挂到全局,测试用它模拟"选中节点";捕获 props 供 treeApi 断言 */
|
||||
@@ -67,6 +74,8 @@ vi.mock("@/components/CategoryTreePanel.vue", () => ({
|
||||
emit("update:curTreeNode", node);
|
||||
emit("on-change", node);
|
||||
};
|
||||
// P-D-01:概览统计要"直属+子孙",用 on-tree-data 把(含虚拟根的)栏目树喂给 CategoryManage.treeData
|
||||
(globalThis as any).__treeEmitData = (list: any[]) => emit("on-tree-data", list);
|
||||
(globalThis as any).__treeProps = props;
|
||||
// CategoryManage 会经模板 ref 调这些方法(真实组件的 exposed API)
|
||||
expose({ loadTree: () => {}, refreshTree: () => {}, reloadTree: () => {} });
|
||||
@@ -126,7 +135,11 @@ async function flush(frames = 5) {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
(globalThis as any).__treeEmit = undefined;
|
||||
(globalThis as any).__treeEmitData = undefined;
|
||||
(globalThis as any).__treeProps = undefined;
|
||||
pageResult.value = { list: [], total: 0 };
|
||||
treeFixture.value = [];
|
||||
routeMock.value = { params: {}, query: {} };
|
||||
currentStore = null;
|
||||
window.open = openMock as any;
|
||||
});
|
||||
@@ -168,6 +181,19 @@ describe("categoryWorkbench - ArticleListPanel(列表型右侧面板)", () =
|
||||
expect(pageMock.mock.calls[1][0]).toMatchObject({ categoryIds: 7 });
|
||||
});
|
||||
|
||||
it("T-D-02 文章页 ?categoryId= 深链:读 query 初始化栏目筛选并回显下拉", async () => {
|
||||
treeFixture.value = [{ id: 5, name: "新闻中心", type: 1, children: [{ id: 51, name: "校园时讯", type: 1 }] }];
|
||||
routeMock.value = { params: {}, query: { categoryId: "5" } };
|
||||
const el = mountApp(defineComponent({
|
||||
setup: () => () => h(ArticleListPanel, {}),
|
||||
}));
|
||||
await flush();
|
||||
|
||||
// query 里的字符串 id 须转成数字:既对齐后端 categoryIds,也让栏目下拉回显选中项
|
||||
expect(pageMock.mock.calls[0][0]).toMatchObject({ siteId: 1, categoryIds: 5 });
|
||||
expect(el.querySelector(".ivu-select-selected-value")?.textContent).toContain("新闻中心");
|
||||
});
|
||||
|
||||
it("⑤ 面板新建文章:非固定模式带 categoryId query 跳转;固定模式入口收敛到工作台面板头(面板内无重复按钮)", async () => {
|
||||
const state = reactive({ cid: 5, fixed: false });
|
||||
const host = defineComponent({
|
||||
@@ -218,15 +244,75 @@ describe("categoryWorkbench - CategoryManage 右侧类型化面板", () => {
|
||||
expect(openMock).toHaveBeenCalledWith("https://www.moe.gov.cn", "_blank");
|
||||
});
|
||||
|
||||
it("列表型栏目:右侧渲染 ArticleListPanel(categoryId=选中栏目)", async () => {
|
||||
it("T-D-01 列表型栏目:右侧降级为「栏目内容概览」(统计 + 最近 5 条只读),不再渲染操作表", async () => {
|
||||
pageResult.value = {
|
||||
total: 12,
|
||||
list: [1, 2, 3, 4, 5, 6].map(i => ({
|
||||
id: 900 + i,
|
||||
title: `云海大学第${i}次党代会筹备推进会`,
|
||||
categoryId: 5,
|
||||
categoryName: "新闻中心",
|
||||
status: 1,
|
||||
publishTime: "2026-09-20 10:00",
|
||||
topmost: 0,
|
||||
})),
|
||||
};
|
||||
const el = mountApp(defineComponent({ render: () => h(CategoryManage) }));
|
||||
await flush();
|
||||
// 统计口径 = 直属 + 子孙栏目:树经 on-tree-data 下发(含 W2 虚拟根),子孙 51/52 须并入计数
|
||||
(globalThis as any).__treeEmitData([
|
||||
{
|
||||
id: 0,
|
||||
name: "演示学校",
|
||||
virtual: true,
|
||||
children: [
|
||||
{
|
||||
id: 5,
|
||||
name: "新闻中心",
|
||||
type: 1,
|
||||
children: [
|
||||
{ id: 51, name: "校园时讯", type: 1 },
|
||||
{ id: 52, name: "通知公告", type: 1 },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
(globalThis as any).__treeEmit({ id: 5, parentId: 0, name: "新闻中心", type: 1, sortNo: 0, status: 1 });
|
||||
await flush();
|
||||
|
||||
const box = el.querySelector(".category-content-summary");
|
||||
expect(box).toBeTruthy();
|
||||
// 完整的文章操作表不再出现在栏目页(唯一操作台是 /article)
|
||||
expect(el.querySelector(".article-list-panel")).toBeNull();
|
||||
expect(box!.querySelector(".ivu-table")).toBeNull();
|
||||
// 概览内不得出现文章操作入口(发布/下架/置顶/删除按钮);状态说明文字不算
|
||||
const affordances = [...box!.querySelectorAll("button, a")].map(n => (n.textContent || "").trim());
|
||||
expect(affordances.some(t => ["发布", "下架", "取消置顶", "置顶", "删除"].includes(t))).toBe(false);
|
||||
|
||||
// 统计查询:直属+子孙(逗号分隔,后端 List<Integer> 契约)、全状态(不带 status)、只要最近 5 条
|
||||
expect(pageMock).toHaveBeenCalled();
|
||||
const q = pageMock.mock.calls[0][0];
|
||||
expect(q).toMatchObject({ siteId: 1, categoryIds: "5,51,52", page: 1, pageSize: 5 });
|
||||
expect(q.status).toBeUndefined();
|
||||
|
||||
expect(box!.textContent).toContain("共 12 篇");
|
||||
// 只读 5 条(第 6 条不渲染)
|
||||
expect(box!.querySelectorAll(".ccs-row")).toHaveLength(5);
|
||||
expect(box!.textContent).toContain("云海大学第1次党代会筹备推进会");
|
||||
});
|
||||
|
||||
it("T-D-03 概览「查看全部 →」跳 /article 深链(带 categoryId 预筛选)", async () => {
|
||||
const el = mountApp(defineComponent({ render: () => h(CategoryManage) }));
|
||||
await flush();
|
||||
(globalThis as any).__treeEmit({ id: 5, parentId: 0, name: "新闻中心", type: 1, sortNo: 0, status: 1 });
|
||||
await flush();
|
||||
|
||||
expect(el.querySelector(".article-list-panel")).toBeTruthy();
|
||||
expect(pageMock).toHaveBeenCalled();
|
||||
expect(pageMock.mock.calls[0][0]).toMatchObject({ categoryIds: 5 });
|
||||
const more = [...el.querySelectorAll("a, button")].find(b => b.textContent?.includes("查看全部"));
|
||||
expect(more).toBeTruthy();
|
||||
more!.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
await flush();
|
||||
expect(routerPush).toHaveBeenCalledWith("/sch/demo1/article?categoryId=5");
|
||||
});
|
||||
|
||||
it("未选中节点:右侧显示空态引导", async () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, onMounted, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { Message, Tag } from "view-ui-plus";
|
||||
import Utils from "@/utils/index";
|
||||
import MyConfirm from "@/components/MyConfirm";
|
||||
@@ -23,6 +23,7 @@ const props = defineProps<{
|
||||
}>();
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const store = useCmsStore();
|
||||
|
||||
const rows = ref<ArticlePageItem[]>([]);
|
||||
@@ -32,7 +33,9 @@ const pageNum = ref(1);
|
||||
const pageSize = ref(parseInt(localStorage.getItem("customPageCount") || "10"));
|
||||
|
||||
const search = reactive({
|
||||
categoryId: undefined as number | undefined,
|
||||
// P-D1:栏目页概览「查看全部 →」以 /article?categoryId={id} 深链进入,读 query 预筛选
|
||||
// (query 是字符串,转数字才能对上 Option 的 value 并回显下拉选中项)
|
||||
categoryId: Number((route.query?.categoryId as any)) || undefined,
|
||||
status: undefined as number | undefined,
|
||||
keyword: "",
|
||||
});
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { Tag } from "view-ui-plus";
|
||||
import { articleApi } from "@/api/module/cms";
|
||||
import { useCmsStore } from "@/store";
|
||||
import { ArticlePageItem } from "@/types/cms";
|
||||
import { adminPathOf } from "@/utils/sitePath";
|
||||
|
||||
/**
|
||||
* 栏目内容概览(P-D1,v2 §B.3):/category 右侧列表型栏目的降级面板。
|
||||
* 定位=只读概览;文章的发布/下架/置顶/删除只在 /article(唯一操作台)做。
|
||||
*
|
||||
* 统计口径(手册 P-D1 定稿,勿自行改):
|
||||
* - 范围 = 选中栏目的**直属 + 全部子孙栏目**。后端 ArticlePageQuery.categoryIds 是不递归的 IN 查询,
|
||||
* 故子孙 id 由前端从栏目树展开(与门户 PortalPageRenderer.collectListDescendantIds 同语义)。
|
||||
* - 状态 = **全状态计数**(草稿/已发布/已下架全部计入,不带 status 过滤;系统内无"回收站"概念)。
|
||||
* - 一次 page 请求同时拿到总数(total)与最近 5 条(list,后端按置顶降序 + 创建时间降序)。
|
||||
*/
|
||||
const props = defineProps<{
|
||||
categoryId: number;
|
||||
categoryName?: string;
|
||||
/** 全量栏目树(CategoryManage 的 treeData,含 W2 虚拟根;按 id 定位,虚拟根 id=0 不会命中真实栏目) */
|
||||
tree?: any[];
|
||||
}>();
|
||||
|
||||
const router = useRouter();
|
||||
const store = useCmsStore();
|
||||
|
||||
const total = ref(0);
|
||||
const rows = ref<ArticlePageItem[]>([]);
|
||||
const loading = ref(false);
|
||||
|
||||
const statusDict = [
|
||||
{ value: 0, label: "草稿", color: "default" },
|
||||
{ value: 1, label: "已发布", color: "success" },
|
||||
{ value: 2, label: "已下架", color: "warning" },
|
||||
];
|
||||
const statusText = (s: number) => statusDict.find(i => i.value === s)?.label || "未知";
|
||||
const statusColor = (s: number) => statusDict.find(i => i.value === s)?.color || "default";
|
||||
|
||||
function findNode(list: any[], id: number): any {
|
||||
for (const n of list || []) {
|
||||
if (Number(n.id) === Number(id)) return n;
|
||||
const hit = findNode(n.children || [], id);
|
||||
if (hit) return hit;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function collectIds(node: any, acc: number[]): number[] {
|
||||
acc.push(Number(node.id));
|
||||
(node.children || []).forEach((c: any) => collectIds(c, acc));
|
||||
return acc;
|
||||
}
|
||||
|
||||
/** 统计范围 id:树未就绪(或未命中)时退化为仅直属栏目 */
|
||||
const scopeIds = computed<number[]>(() => {
|
||||
const node = findNode(props.tree || [], props.categoryId);
|
||||
return node ? collectIds(node, []) : [Number(props.categoryId)];
|
||||
});
|
||||
const scopeKey = computed(() => scopeIds.value.join(","));
|
||||
|
||||
async function run() {
|
||||
if (!store.siteId) return;
|
||||
loading.value = true;
|
||||
try {
|
||||
const page = await articleApi
|
||||
.page({
|
||||
siteId: store.siteId,
|
||||
categoryIds: scopeKey.value,
|
||||
page: 1,
|
||||
pageSize: 5,
|
||||
})
|
||||
.send();
|
||||
total.value = page?.total || 0;
|
||||
rows.value = (page?.list || []).slice(0, 5);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 跳 /article 深链预筛选(子栏目行按自身栏目过滤,父栏目"查看全部"按父栏目过滤) */
|
||||
function goArticles(categoryId?: number) {
|
||||
const code = store.schoolCode;
|
||||
if (!code) return;
|
||||
const id = categoryId ?? props.categoryId;
|
||||
router.push(adminPathOf(code, store.siteSegCode, "article") + `?categoryId=${id}`);
|
||||
}
|
||||
|
||||
// 树稍后到达(子孙变化)与切换栏目都重取;站点切换由 CategoryManage 清选中后重建本组件
|
||||
watch(scopeKey, run);
|
||||
onMounted(run);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="category-content-summary">
|
||||
<div class="ccs-stat fix">
|
||||
<span class="ccs-total">共 <b>{{ total }}</b> 篇</span>
|
||||
<span class="ccs-scope">
|
||||
{{ categoryName || "本栏目" }}及子栏目({{ scopeIds.length }} 个栏目)· 草稿/已发布/已下架全部计入
|
||||
</span>
|
||||
<Button class="fr" size="small" @click="goArticles()">查看全部 →</Button>
|
||||
</div>
|
||||
|
||||
<div v-if="loading" class="ccs-empty">加载中…</div>
|
||||
<ul v-else-if="rows.length" class="ccs-list">
|
||||
<li v-for="row in rows" :key="row.id" class="ccs-row fix">
|
||||
<a class="ccs-title operating-link" :title="row.title" @click="goArticles(row.categoryId)">{{ row.title }}</a>
|
||||
<Tag class="ccs-tag" :color="statusColor(row.status)">{{ statusText(row.status) }}</Tag>
|
||||
<span class="ccs-time">{{ row.publishTime || row.createTime || "-" }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-else class="ccs-empty">
|
||||
<p>本栏目及子栏目下暂无文章</p>
|
||||
<p class="ccs-empty-tip">可在上方「新建文章」直接发布到本栏目,或到「文章列表」页检索全站内容。</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.category-content-summary {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
padding: 4px 16px 16px;
|
||||
}
|
||||
|
||||
.ccs-stat {
|
||||
padding: 14px 0 10px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.ccs-total {
|
||||
font-size: 14px;
|
||||
color: #515a6e;
|
||||
|
||||
b {
|
||||
font-size: 20px;
|
||||
color: #2d8cf0;
|
||||
margin: 0 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.ccs-scope {
|
||||
margin-left: 10px;
|
||||
font-size: 12px;
|
||||
color: #808695;
|
||||
}
|
||||
|
||||
.ccs-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ccs-row {
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px dashed #f0f0f0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ccs-title {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ccs-tag {
|
||||
flex: none;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.ccs-time {
|
||||
flex: none;
|
||||
font-size: 12px;
|
||||
color: #808695;
|
||||
}
|
||||
|
||||
.ccs-empty {
|
||||
padding: 32px 0;
|
||||
text-align: center;
|
||||
color: #808695;
|
||||
}
|
||||
|
||||
.ccs-empty-tip {
|
||||
margin-top: 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
+1
-1
@@ -170,7 +170,7 @@ export interface ArticleSaveRequest {
|
||||
export interface ArticlePageQuery {
|
||||
siteId: number;
|
||||
/** 栏目 id(后端字段 categoryIds: List<Integer>,多选逗号分隔;空=全部栏目) */
|
||||
categoryIds?: number;
|
||||
categoryIds?: number | string;
|
||||
status?: number;
|
||||
keyword?: string;
|
||||
page?: number;
|
||||
|
||||
@@ -8,7 +8,7 @@ import CategoryTreePanel from "@/components/CategoryTreePanel.vue";
|
||||
import CategorySortModal from "@/components/CategorySortModal.vue";
|
||||
import BatchAddCategory from "@/components/BatchAddCategory.vue";
|
||||
import SvgIcon from "@/components/SvgIcon.vue";
|
||||
import ArticleListPanel from "@/components/article/ArticleListPanel.vue";
|
||||
import CategoryContentSummary from "@/components/category/CategoryContentSummary.vue";
|
||||
import { categoryApi } from "@/api/module/cms";
|
||||
import { useCmsStore } from "@/store";
|
||||
import config from "@/assets/config/host.config";
|
||||
@@ -17,7 +17,9 @@ import { rootLabelOf, wrapTreeWithRoot } from "@/utils/categoryTree";
|
||||
import { portalUrlOf, adminPathOf } from "@/utils/sitePath";
|
||||
|
||||
/* UserBase group/index 同款交互:图标工具栏(新增/编辑/子组排序/删除)+ ztree(W2:搜索/收起展开按钮移除)。
|
||||
B5 栏目工作台:右侧类型化面板(列表=文章表格 / 单页=富文本预览 / 外链=直达)。 */
|
||||
B5 栏目工作台:右侧类型化面板(单页=富文本预览 / 外链=直达)。
|
||||
P-D1 内容管理 IA 切分:列表型栏目右侧降级为「栏目内容概览」(只读统计 + 最近 5 条),
|
||||
文章的检索与操作统一在 /article(内容视图)。 */
|
||||
const store = useCmsStore();
|
||||
|
||||
const refTreePanel = useTemplateRef("refTreePanel");
|
||||
@@ -440,13 +442,13 @@ function openLink(url: string) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 列表型:本栏目文章管理(联动仅经 categoryId watch,拖拽排序不触发) -->
|
||||
<ArticleListPanel
|
||||
<!-- 列表型:只读内容概览(P-D1 结构/内容视图切分;文章操作统一在 /article) -->
|
||||
<CategoryContentSummary
|
||||
v-if="selected.type === 1"
|
||||
class="panel-body"
|
||||
:category-id="selected.id"
|
||||
:extra-height="78"
|
||||
fixed-category
|
||||
:category-name="selected.name"
|
||||
:tree="treeData"
|
||||
/>
|
||||
|
||||
<!-- 单页型:正文渲染预览 + 元信息 + 编辑入口 -->
|
||||
@@ -551,7 +553,7 @@ function openLink(url: string) {
|
||||
<Select v-model="form.homeStyle" placeholder="默认卡片">
|
||||
<Option v-for="s in homeStyleDict" :key="s.value" :value="s.value">{{ s.label }}</Option>
|
||||
</Select>
|
||||
<p class="form-tip">首页该栏目区块的呈现形态;「双栏新闻卡/媒体聚焦列表」需皮肤包支持(如炫彩校园),旧主题按默认卡展示。也可在「站点设置 → 首页布局」里按区块单独指定形态(以那里为准)。</p>
|
||||
<p class="form-tip">首页该栏目区块的呈现形态;展示形态由当前皮肤包的主题化实现决定,未实现的形态回落通用骨架渲染。也可在「站点设置 → 首页布局」里按区块单独指定形态(以那里为准)。</p>
|
||||
</FormItem>
|
||||
|
||||
<Divider size="small" orientation="left">SEO 设置</Divider>
|
||||
|
||||
Reference in New Issue
Block a user