feat(web): 对齐 Alright 主题视觉规范(配色/字体/图标/背景/动效),并补充设计规范文档

This commit is contained in:
2026-10-22 20:27:10 +08:00
parent e522a98f6f
commit 386c0f77c7
50 changed files with 6344 additions and 1094 deletions
@@ -0,0 +1,88 @@
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>
)
}