89 lines
2.8 KiB
TypeScript
89 lines
2.8 KiB
TypeScript
import { categoryCount, type Category } from '../../data/types'
|
||
import { ArrowLeftIcon } from '../common/Icon'
|
||
import { AppGrid } from './AppGrid'
|
||
import { CesCardGrid } from './CesCardGrid'
|
||
import { GroupBlock } from './GroupBlock'
|
||
import { PosterGrid } from './PosterGrid'
|
||
|
||
/**
|
||
* 分类明细视图:返回条 + 面包屑 + hero(图标 / 标题 / 数量)+ 内容区。
|
||
*
|
||
* 内容区按分类类型切换:
|
||
* - apps:指定分组时只渲染该组,否则单组扁平渲染、多组带小标题分组渲染;
|
||
* - posters:6 列海报;
|
||
* - articles:上图下文白卡网格。
|
||
*/
|
||
type DetailViewProps = {
|
||
category: Category
|
||
/** 从侧栏子菜单进入时指定的分组 */
|
||
groupId?: string
|
||
onBack: () => void
|
||
/** 点击卡片 / 海报进入条目详情 */
|
||
onOpenItem: (itemId: string) => void
|
||
}
|
||
|
||
export function DetailView({ category, groupId, onBack, onOpenItem }: DetailViewProps) {
|
||
const groups = category.kind === 'apps' ? category.groups : []
|
||
const activeGroup =
|
||
groupId !== undefined ? groups.find((group) => group.id === groupId) : undefined
|
||
|
||
const title = activeGroup?.name ?? category.title
|
||
const count = activeGroup !== undefined ? activeGroup.items.length : categoryCount(category)
|
||
const breadcrumb =
|
||
activeGroup !== undefined
|
||
? `首页 / ${category.title} / ${activeGroup.name}`
|
||
: `首页 / ${category.title}`
|
||
|
||
function renderBody() {
|
||
if (category.kind === 'posters') {
|
||
return <PosterGrid items={category.items} variant="detail" onOpen={onOpenItem} />
|
||
}
|
||
|
||
if (category.kind === 'articles') {
|
||
return <CesCardGrid items={category.items} onOpen={onOpenItem} />
|
||
}
|
||
|
||
if (activeGroup !== undefined) {
|
||
return <AppGrid items={activeGroup.items} variant="detail" onOpen={onOpenItem} />
|
||
}
|
||
|
||
if (category.groups.length === 1) {
|
||
return <AppGrid items={category.groups[0].items} variant="detail" onOpen={onOpenItem} />
|
||
}
|
||
|
||
if (category.groups.length === 0) {
|
||
return <p className="section-empty">{category.emptyText ?? '暂无内容'}</p>
|
||
}
|
||
|
||
return (
|
||
<>
|
||
{category.groups.map((group) => (
|
||
<GroupBlock key={group.id} group={group} onOpen={onOpenItem} />
|
||
))}
|
||
</>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<div className="detail-view">
|
||
<div className="detail-nav card-box">
|
||
<button type="button" className="detail-back" onClick={onBack}>
|
||
<ArrowLeftIcon />
|
||
返回首页
|
||
</button>
|
||
<span className="detail-breadcrumb">{breadcrumb}</span>
|
||
</div>
|
||
|
||
<div className="detail-hero">
|
||
<span className="detail-hero-icon" aria-hidden="true">
|
||
{category.icon}
|
||
</span>
|
||
<h2 className="detail-hero-title">{title}</h2>
|
||
<span className="detail-hero-count">[{count}]</span>
|
||
</div>
|
||
|
||
{renderBody()}
|
||
</div>
|
||
)
|
||
}
|