feat(content): 分组对所有内容类型生效,统一分区/分组/条目模型
- 分区=容器,分组=任意类型的可选子容器,条目可挂分区或分组(直属分区) - catalog 所有类型统一输出 groups + items,条目查询按节点取 - 详情/侧栏/首页卡片预览支持所有类型的分组;多组显示小标题 + 未分组 - 后台「分区」「侧栏导航」对所有类型可建组;「内容」弹窗分组可选直属分区,编辑可换组 - 修复首页卡片配置为「整个分区」时预览为空 - 测试 59 passed
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
import { Breadcrumb, Card, Empty, Space, Title } from '@stash/ui'
|
||||
import { categoryCount, type Category } from '../../data/types'
|
||||
import {
|
||||
categoryCount,
|
||||
type AppItem,
|
||||
type ArticleItem,
|
||||
type Category,
|
||||
type PosterItem,
|
||||
} from '../../data/types'
|
||||
import { viewPath } from '../../data/view'
|
||||
import { AppGrid } from './AppGrid'
|
||||
import { CesCardGrid } from './CesCardGrid'
|
||||
@@ -25,7 +31,13 @@ type DetailViewProps = {
|
||||
}
|
||||
|
||||
export function DetailView({ category, groupId, onNavigate, onOpenItem }: DetailViewProps) {
|
||||
const groups = category.kind === 'apps' ? category.groups : []
|
||||
// 三种类型共用「分区 → 分组 → 条目」结构;这里抹平联合类型,渲染时再按 kind 分派
|
||||
type RenderItem = AppItem | PosterItem | ArticleItem
|
||||
const groups = category.groups as {
|
||||
id: string
|
||||
name: string
|
||||
items: RenderItem[]
|
||||
}[]
|
||||
const activeGroup =
|
||||
groupId !== undefined ? groups.find((group) => group.id === groupId) : undefined
|
||||
|
||||
@@ -43,32 +55,48 @@ export function DetailView({ category, groupId, onNavigate, onOpenItem }: Detail
|
||||
{ label: category.title, href: viewPath({ type: 'detail', categoryId: category.id }) },
|
||||
]
|
||||
|
||||
function renderBody() {
|
||||
/** 按类型渲染一组条目 */
|
||||
function renderItems(items: RenderItem[]) {
|
||||
if (category.kind === 'posters') {
|
||||
return <PosterGrid items={category.items} variant="detail" onOpen={onOpenItem} />
|
||||
return <PosterGrid items={items as PosterItem[]} variant="detail" onOpen={onOpenItem} />
|
||||
}
|
||||
|
||||
if (category.kind === 'articles') {
|
||||
return <CesCardGrid items={category.items} onOpen={onOpenItem} />
|
||||
return <CesCardGrid items={items as ArticleItem[]} onOpen={onOpenItem} />
|
||||
}
|
||||
|
||||
return <AppGrid items={items as AppItem[]} variant="detail" onOpen={onOpenItem} />
|
||||
}
|
||||
|
||||
function renderBody() {
|
||||
if (activeGroup !== undefined) {
|
||||
return <AppGrid items={activeGroup.items} variant="detail" onOpen={onOpenItem} />
|
||||
return renderItems(activeGroup.items)
|
||||
}
|
||||
|
||||
if (category.groups.length === 1) {
|
||||
return <AppGrid items={category.groups[0].items} variant="detail" onOpen={onOpenItem} />
|
||||
const direct = (category.items ?? []) as RenderItem[]
|
||||
|
||||
if (groups.length === 0) {
|
||||
return direct.length > 0 ? (
|
||||
renderItems(direct)
|
||||
) : (
|
||||
<Empty text={'emptyText' in category ? (category.emptyText ?? '暂无内容') : '暂无内容'} />
|
||||
)
|
||||
}
|
||||
|
||||
if (category.groups.length === 0) {
|
||||
return <Empty text={category.emptyText ?? '暂无内容'} />
|
||||
if (groups.length === 1 && direct.length === 0) {
|
||||
return renderItems(groups[0].items)
|
||||
}
|
||||
|
||||
return (
|
||||
<Space direction="column" gap="lg" align="stretch">
|
||||
{category.groups.map((group) => (
|
||||
<GroupBlock key={group.id} group={group} onOpen={onOpenItem} />
|
||||
{groups.map((group) => (
|
||||
<GroupBlock key={group.id} name={group.name}>
|
||||
{renderItems(group.items)}
|
||||
</GroupBlock>
|
||||
))}
|
||||
{direct.length > 0 && (
|
||||
<GroupBlock name="未分组">{renderItems(direct)}</GroupBlock>
|
||||
)}
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user