feat(content): 分组对所有内容类型生效,统一分区/分组/条目模型
- 分区=容器,分组=任意类型的可选子容器,条目可挂分区或分组(直属分区) - catalog 所有类型统一输出 groups + items,条目查询按节点取 - 详情/侧栏/首页卡片预览支持所有类型的分组;多组显示小标题 + 未分组 - 后台「分区」「侧栏导航」对所有类型可建组;「内容」弹窗分组可选直属分区,编辑可换组 - 修复首页卡片配置为「整个分区」时预览为空 - 测试 59 passed
This commit is contained in:
+42
-12
@@ -31,14 +31,16 @@ export type AppItem = {
|
||||
}
|
||||
|
||||
/** 应用分组:明细页按组展示,首页按展示块取其中一组做预览 */
|
||||
export type AppGroup = {
|
||||
export type ContentGroup<T> = {
|
||||
id: string
|
||||
name: string
|
||||
/** 是否出现在侧栏;缺省视为 true */
|
||||
showInSidebar?: boolean
|
||||
items: AppItem[]
|
||||
items: T[]
|
||||
}
|
||||
|
||||
export type AppGroup = ContentGroup<AppItem>
|
||||
|
||||
/** 竖版海报条目(电影) */
|
||||
export type PosterItem = {
|
||||
id: string
|
||||
@@ -97,6 +99,8 @@ export type AppCategory = {
|
||||
/** 明细页 hero 区的字符图标 */
|
||||
icon: string
|
||||
groups: AppGroup[]
|
||||
/** 直接挂在分区上的条目(未分组) */
|
||||
items?: AppItem[]
|
||||
/** 首页预览条数,默认 4 */
|
||||
previewLimit?: number
|
||||
/** 首页预览为空时的提示文案 */
|
||||
@@ -114,6 +118,8 @@ export type PosterCategory = {
|
||||
title: string
|
||||
barColor: string
|
||||
icon: string
|
||||
groups: ContentGroup<PosterItem>[]
|
||||
/** 直接挂在分区上的条目(未分组) */
|
||||
items: PosterItem[]
|
||||
/** 首页预览条数,默认 6 */
|
||||
previewLimit?: number
|
||||
@@ -130,6 +136,8 @@ export type ArticleCategory = {
|
||||
title: string
|
||||
barColor: string
|
||||
icon: string
|
||||
groups: ContentGroup<ArticleItem>[]
|
||||
/** 直接挂在分区上的条目(未分组) */
|
||||
items: ArticleItem[]
|
||||
/** 首页预览条数,默认 4 */
|
||||
previewLimit?: number
|
||||
@@ -141,23 +149,28 @@ export type ArticleCategory = {
|
||||
|
||||
export type Category = AppCategory | PosterCategory | ArticleCategory
|
||||
|
||||
/** 分类内的条目总数,用于角标与明细页计数 */
|
||||
/** 分类内的条目总数(直属 + 分组的),用于角标与明细页计数 */
|
||||
export function categoryCount(category: Category): number {
|
||||
if (category.kind === 'apps') {
|
||||
return category.groups.reduce((total, group) => total + group.items.length, 0)
|
||||
}
|
||||
return category.items.length
|
||||
const direct = category.items?.length ?? 0
|
||||
const grouped = category.groups.reduce((total, group) => total + group.items.length, 0)
|
||||
return direct + grouped
|
||||
}
|
||||
|
||||
/** 把应用类分类的分组拍平,首页预览用 */
|
||||
/** 把应用类分类的条目拍平(分组 + 直属),首页预览用 */
|
||||
export function flattenAppItems(category: AppCategory): AppItem[] {
|
||||
return category.groups.flatMap((group) => group.items)
|
||||
return [
|
||||
...category.groups.flatMap((group) => group.items),
|
||||
...(category.items ?? []),
|
||||
]
|
||||
}
|
||||
|
||||
/** 按 id 在分类里查找条目;详情页用 */
|
||||
export function findAppItem(category: Category, itemId: string): AppItem | undefined {
|
||||
if (category.kind !== 'apps') return undefined
|
||||
|
||||
const direct = (category.items ?? []).find((item) => item.id === itemId)
|
||||
if (direct !== undefined) return direct
|
||||
|
||||
for (const group of category.groups) {
|
||||
const found = group.items.find((item) => item.id === itemId)
|
||||
if (found !== undefined) return found
|
||||
@@ -184,7 +197,13 @@ export function findPosterItem(
|
||||
itemId: string,
|
||||
): PosterItem | undefined {
|
||||
if (category.kind !== 'posters') return undefined
|
||||
return category.items.find((item) => item.id === itemId)
|
||||
const direct = category.items.find((item) => item.id === itemId)
|
||||
if (direct !== undefined) return direct
|
||||
for (const group of category.groups) {
|
||||
const found = group.items.find((item) => item.id === itemId)
|
||||
if (found !== undefined) return found
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
/** 取同分类下的其他海报,用于详情页的「推荐阅读」 */
|
||||
@@ -195,7 +214,12 @@ export function relatedPosterItems(
|
||||
): PosterItem[] {
|
||||
if (category.kind !== 'posters') return []
|
||||
|
||||
return category.items.filter((item) => item.id !== excludeId).slice(0, limit)
|
||||
return [
|
||||
...category.items,
|
||||
...category.groups.flatMap((group) => group.items),
|
||||
]
|
||||
.filter((item) => item.id !== excludeId)
|
||||
.slice(0, limit)
|
||||
}
|
||||
|
||||
/** 按 id 查找图文条目 */
|
||||
@@ -204,5 +228,11 @@ export function findArticleItem(
|
||||
itemId: string,
|
||||
): ArticleItem | undefined {
|
||||
if (category.kind !== 'articles') return undefined
|
||||
return category.items.find((item) => item.id === itemId)
|
||||
const direct = category.items.find((item) => item.id === itemId)
|
||||
if (direct !== undefined) return direct
|
||||
for (const group of category.groups) {
|
||||
const found = group.items.find((item) => item.id === itemId)
|
||||
if (found !== undefined) return found
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user