fix(web): 恢复与旧版一致的布局与组件参数
- 布局:Layout 使用 contained(1380)+gutter20+siderWidth240+gap20、solid 60px 页头、整屏内部滚动 - 搜索框恢复悬浮居中;侧栏与内容恢复 52/88px 顶部偏移 - 新增并使用 @stash/ui 的 Cover(海报 2:3 / 图文 4:3 / 宽图)替换手写封面 - 头像恢复 64px 品牌底色;正文恢复 14/1.9;详情标题、标签、引用、统计、跳转按原版还原 - 分区卡 padding 20、分区标题 16、网格间距 12/16 对齐原值
This commit is contained in:
@@ -2,7 +2,7 @@ import { Grid } from '@stash/ui'
|
||||
import type { AppItem } from '../../data/types'
|
||||
import { AppCard } from './AppCard'
|
||||
|
||||
/** 应用网格:默认 4 列,平板 2 列,手机 2 列(组件库 Grid 负责断点) */
|
||||
/** 应用网格:桌面 4 列,平板 / 手机 2 列(断点由组件库 Grid 负责) */
|
||||
type AppGridProps = {
|
||||
items: AppItem[]
|
||||
/** 明细页使用更宽松的间距 */
|
||||
@@ -13,7 +13,12 @@ type AppGridProps = {
|
||||
|
||||
export function AppGrid({ items, variant = 'home', onOpen }: AppGridProps) {
|
||||
return (
|
||||
<Grid columns={4} tabletColumns={2} mobileColumns={2} gap={variant === 'detail' ? 'md' : 'sm'}>
|
||||
<Grid
|
||||
columns={4}
|
||||
tabletColumns={2}
|
||||
mobileColumns={2}
|
||||
gap={variant === 'detail' ? 'lg' : 'md'}
|
||||
>
|
||||
{items.map((item) => (
|
||||
<AppCard key={item.id} item={item} onOpen={onOpen} />
|
||||
))}
|
||||
|
||||
@@ -123,7 +123,7 @@ export function ArticleBody({ markdown }: ArticleBodyProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Paragraph tone="main" key={index}>
|
||||
<Paragraph tone="sub" size="md" leading="relaxed" key={index}>
|
||||
{renderInline(block.text)}
|
||||
</Paragraph>
|
||||
)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Cover } from '@stash/ui'
|
||||
import type { ArticleItem } from '../../data/types'
|
||||
|
||||
/**
|
||||
@@ -10,20 +11,13 @@ type ArticleCardProps = {
|
||||
}
|
||||
|
||||
export function ArticleCard({ item, onOpen }: ArticleCardProps) {
|
||||
const content = (
|
||||
<>
|
||||
<img className="cover cover--article" src={item.cover} alt={item.title} loading="lazy" />
|
||||
<span className="cover-caption">{item.title}</span>
|
||||
</>
|
||||
)
|
||||
|
||||
if (onOpen === undefined) {
|
||||
return <figure className="cover-card">{content}</figure>
|
||||
}
|
||||
|
||||
return (
|
||||
<button type="button" className="cover-card" title={item.title} onClick={() => onOpen(item.id)}>
|
||||
{content}
|
||||
</button>
|
||||
<Cover
|
||||
src={item.cover}
|
||||
alt={item.title}
|
||||
caption={item.title}
|
||||
ratio="article"
|
||||
onClick={onOpen === undefined ? undefined : () => onOpen(item.id)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button, Card, Paragraph, Space, Text, Title } from '@stash/ui'
|
||||
import { Card, Paragraph, Space, Text, Title } from '@stash/ui'
|
||||
import type { ArticleItem } from '../../data/types'
|
||||
import { ArticleBody } from './ArticleBody'
|
||||
|
||||
@@ -20,23 +20,23 @@ export function ArticleDetailView({ article, categoryTitle, onBack }: ArticleDet
|
||||
|
||||
return (
|
||||
<div className="detail-view article-view">
|
||||
<Card padding="sm">
|
||||
<Card padding="none" className="detail-nav">
|
||||
<Space justify="between" wrap>
|
||||
<Button variant="ghost" size="sm" onClick={onBack}>
|
||||
<button type="button" className="detail-back" onClick={onBack}>
|
||||
返回
|
||||
</Button>
|
||||
<Text size="sm" tone="sub">
|
||||
</button>
|
||||
<Text size="xs" tone="muted">
|
||||
{categoryTitle} / 文章
|
||||
</Text>
|
||||
</Space>
|
||||
</Card>
|
||||
|
||||
<header className="article-head">
|
||||
<Title level={2}>
|
||||
<Title level={1} as="h2" className="article-heading">
|
||||
{article.title}
|
||||
<Button variant="ghost" size="sm" disabled className="item-edit">
|
||||
编辑
|
||||
</Button>
|
||||
<button type="button" className="item-edit" disabled title="编辑功能待接入">
|
||||
[编辑]
|
||||
</button>
|
||||
</Title>
|
||||
|
||||
<Space gap="sm" wrap>
|
||||
@@ -55,9 +55,9 @@ export function ArticleDetailView({ article, categoryTitle, onBack }: ArticleDet
|
||||
</Space>
|
||||
</header>
|
||||
|
||||
<Card padding="lg">
|
||||
<Card padding="none" className="article-body-card">
|
||||
<div className="article-body">
|
||||
<Paragraph tone="main" className="article-lead">
|
||||
<Paragraph tone="main" size="md" leading="relaxed" className="article-lead">
|
||||
{article.desc}
|
||||
</Paragraph>
|
||||
<ArticleBody markdown={article.body} />
|
||||
|
||||
@@ -11,7 +11,7 @@ type ArticleGridProps = {
|
||||
|
||||
export function ArticleGrid({ items, onOpen }: ArticleGridProps) {
|
||||
return (
|
||||
<Grid columns={4} tabletColumns={2} mobileColumns={1} gap="sm">
|
||||
<Grid columns={4} tabletColumns={2} mobileColumns={1} gap="md">
|
||||
{items.map((item) => (
|
||||
<ArticleCard key={item.id} item={item} onOpen={onOpen} />
|
||||
))}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Card, Space, Text } from '@stash/ui'
|
||||
import { Card, Cover, Space, Text } from '@stash/ui'
|
||||
import type { ArticleItem } from '../../data/types'
|
||||
|
||||
/**
|
||||
@@ -24,7 +24,14 @@ export function CesCard({ item, onOpen }: CesCardProps) {
|
||||
|
||||
return (
|
||||
<Card padding="none" hoverable className="ces-card">
|
||||
<img className="cover cover--article" src={item.cover} alt={item.title} loading="lazy" />
|
||||
<Cover
|
||||
src={item.cover}
|
||||
alt={item.title}
|
||||
ratio="article"
|
||||
radius="none"
|
||||
hoverZoom
|
||||
onClick={onOpen === undefined ? undefined : () => onOpen(item.id)}
|
||||
/>
|
||||
{onOpen === undefined ? (
|
||||
<div className="ces-body">{body}</div>
|
||||
) : (
|
||||
|
||||
@@ -11,7 +11,7 @@ type CesCardGridProps = {
|
||||
|
||||
export function CesCardGrid({ items, onOpen }: CesCardGridProps) {
|
||||
return (
|
||||
<Grid columns={4} tabletColumns={2} mobileColumns={1} gap="sm">
|
||||
<Grid columns={4} tabletColumns={2} mobileColumns={1} gap="lg">
|
||||
{items.map((item) => (
|
||||
<CesCard key={item.id} item={item} onOpen={onOpen} />
|
||||
))}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Badge, Breadcrumb, Button, Card, Empty, Space, Title } from '@stash/ui'
|
||||
import { Badge, Breadcrumb, Card, Empty, Space, Title } from '@stash/ui'
|
||||
import { categoryCount, type Category } from '../../data/types'
|
||||
import { AppGrid } from './AppGrid'
|
||||
import { CesCardGrid } from './CesCardGrid'
|
||||
@@ -70,11 +70,11 @@ export function DetailView({ category, groupId, onBack, onOpenItem }: DetailView
|
||||
|
||||
return (
|
||||
<div className="detail-view">
|
||||
<Card padding="sm">
|
||||
<Card padding="none" className="detail-nav">
|
||||
<Space justify="between" wrap>
|
||||
<Button variant="ghost" size="sm" onClick={onBack}>
|
||||
<button type="button" className="detail-back" onClick={onBack}>
|
||||
返回首页
|
||||
</Button>
|
||||
</button>
|
||||
<Breadcrumb items={crumbs} />
|
||||
</Space>
|
||||
</Card>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button, Card, Icon, IconButton, Paragraph, Space, Tag, Text, Title } from '@stash/ui'
|
||||
import { Card, Icon, IconButton, Paragraph, Space, Tag, Text, Title } from '@stash/ui'
|
||||
import type { ItemDetailModel } from '../../data/detail'
|
||||
import type { RelatedItem } from '../../data/types'
|
||||
import { RelatedList } from './RelatedList'
|
||||
@@ -27,23 +27,25 @@ export function ItemDetailView({
|
||||
}: ItemDetailViewProps) {
|
||||
return (
|
||||
<div className="detail-view item-view">
|
||||
<Card padding="sm">
|
||||
<Card padding="none" className="detail-nav">
|
||||
<Space justify="between" wrap>
|
||||
<Button variant="ghost" size="sm" onClick={onBack}>
|
||||
<button type="button" className="detail-back" onClick={onBack}>
|
||||
返回
|
||||
</Button>
|
||||
<Text size="sm" tone="sub">
|
||||
{breadcrumb}
|
||||
</Text>
|
||||
</button>
|
||||
<span className="detail-breadcrumb">
|
||||
<Text size="xs" tone="muted">
|
||||
{breadcrumb}
|
||||
</Text>
|
||||
</span>
|
||||
</Space>
|
||||
</Card>
|
||||
|
||||
<Card padding="lg">
|
||||
<Title level={2}>
|
||||
<Card padding="none" className="item-card">
|
||||
<Title level={2} className="item-title">
|
||||
{model.title}
|
||||
<Button variant="ghost" size="sm" disabled className="item-edit">
|
||||
编辑
|
||||
</Button>
|
||||
<button type="button" className="item-edit" disabled title="编辑功能待接入">
|
||||
[编辑]
|
||||
</button>
|
||||
</Title>
|
||||
|
||||
<div className={`item-cover item-cover--${model.shape}`}>
|
||||
@@ -57,7 +59,7 @@ export function ItemDetailView({
|
||||
{model.tags.length > 0 && (
|
||||
<Space gap="sm" wrap>
|
||||
{model.tags.map((tag) => (
|
||||
<Tag key={tag} shape="rounded">
|
||||
<Tag key={tag} tone="primary">
|
||||
{tag}
|
||||
</Tag>
|
||||
))}
|
||||
@@ -65,9 +67,12 @@ export function ItemDetailView({
|
||||
)}
|
||||
|
||||
{model.comment !== undefined && (
|
||||
<Paragraph tone="secondary" className="item-quote">
|
||||
{model.comment}
|
||||
</Paragraph>
|
||||
<p className="item-quote">
|
||||
<span className="item-quote-mark" aria-hidden="true">
|
||||
“
|
||||
</span>
|
||||
<span className="item-quote-text">{model.comment}</span>
|
||||
</p>
|
||||
)}
|
||||
|
||||
{model.paragraphs.map((paragraph, index) => (
|
||||
@@ -76,36 +81,36 @@ export function ItemDetailView({
|
||||
</Paragraph>
|
||||
))}
|
||||
|
||||
<Space justify="between" wrap className="item-actions">
|
||||
<Space gap="lg">
|
||||
<div className="item-actions">
|
||||
<Space gap="sm">
|
||||
<Space gap="xs" className="item-stat">
|
||||
<Icon name="eye_line" />
|
||||
<Text size="sm" tone="sub">
|
||||
<Text size="xs" tone="sub">
|
||||
{model.views}
|
||||
</Text>
|
||||
</Space>
|
||||
<Space gap="xs" className="item-stat">
|
||||
<Icon name="thumb_up_2_line" />
|
||||
<Text size="sm" tone="sub">
|
||||
<Text size="xs" tone="sub">
|
||||
{model.likes}
|
||||
</Text>
|
||||
</Space>
|
||||
<IconButton label="分享(待接入)" size="xs" icon={<Icon name="share_3_line" />} />
|
||||
<IconButton label="分享(待接入)" size="sm" icon={<Icon name="share_3_line" />} />
|
||||
</Space>
|
||||
|
||||
{/* 占位数据没有真实地址时,按钮置灰而不是打开空白页 */}
|
||||
{model.href === '#' ? (
|
||||
<Button size="sm" disabled>
|
||||
<button type="button" className="item-jump" disabled title="暂无跳转地址">
|
||||
跳转到
|
||||
<Icon name="a-external_link_line1" />
|
||||
</Button>
|
||||
</button>
|
||||
) : (
|
||||
<a className="link-button" href={model.href} target="_blank" rel="noreferrer">
|
||||
<a className="item-jump" href={model.href} target="_blank" rel="noreferrer">
|
||||
跳转到
|
||||
<Icon name="a-external_link_line1" />
|
||||
</a>
|
||||
)}
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
{model.banner !== undefined && (
|
||||
<img className="item-banner" src={model.banner} alt="" loading="lazy" />
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Cover } from '@stash/ui'
|
||||
import type { PosterItem } from '../../data/types'
|
||||
|
||||
/**
|
||||
@@ -10,20 +11,13 @@ type PosterCardProps = {
|
||||
}
|
||||
|
||||
export function PosterCard({ item, onOpen }: PosterCardProps) {
|
||||
const content = (
|
||||
<>
|
||||
<img className="cover cover--poster" src={item.cover} alt={item.title} loading="lazy" />
|
||||
<span className="cover-caption">{item.title}</span>
|
||||
</>
|
||||
)
|
||||
|
||||
if (onOpen === undefined) {
|
||||
return <figure className="cover-card">{content}</figure>
|
||||
}
|
||||
|
||||
return (
|
||||
<button type="button" className="cover-card" title={item.title} onClick={() => onOpen(item.id)}>
|
||||
{content}
|
||||
</button>
|
||||
<Cover
|
||||
src={item.cover}
|
||||
alt={item.title}
|
||||
caption={item.title}
|
||||
ratio="poster"
|
||||
onClick={onOpen === undefined ? undefined : () => onOpen(item.id)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ type PosterGridProps = {
|
||||
|
||||
export function PosterGrid({ items, onOpen }: PosterGridProps) {
|
||||
return (
|
||||
<Grid columns={6} tabletColumns={4} mobileColumns={3} gap="sm">
|
||||
<Grid columns={6} tabletColumns={4} mobileColumns={3} gap="md">
|
||||
{items.map((item) => (
|
||||
<PosterCard key={item.id} item={item} onOpen={onOpen} />
|
||||
))}
|
||||
|
||||
@@ -14,12 +14,13 @@ export function RelatedList({ items, onOpen }: RelatedListProps) {
|
||||
if (items.length === 0) return null
|
||||
|
||||
return (
|
||||
<Card padding="lg" className="related-card">
|
||||
<Title level={4} as="h3">
|
||||
<Card padding="md" className="related-card">
|
||||
<Title level={3} as="h3" className="related-title">
|
||||
<span aria-hidden="true">📚</span>
|
||||
推荐阅读
|
||||
</Title>
|
||||
|
||||
<Grid columns={2} tabletColumns={2} mobileColumns={1} gap="sm">
|
||||
<Grid className="related-grid" columns={2} tabletColumns={2} mobileColumns={1} gap="sm">
|
||||
{items.map((item) => (
|
||||
<button
|
||||
key={item.id}
|
||||
@@ -27,7 +28,12 @@ export function RelatedList({ items, onOpen }: RelatedListProps) {
|
||||
className="related-item"
|
||||
onClick={() => onOpen(item.id)}
|
||||
>
|
||||
<Avatar size="sm" shape="square" src={item.cover} fallback={item.icon ?? '•'} />
|
||||
<Avatar
|
||||
size="md"
|
||||
tone="primary"
|
||||
src={item.cover}
|
||||
fallback={item.icon ?? '•'}
|
||||
/>
|
||||
<Space direction="column" gap="xs" align="start" className="related-info">
|
||||
<Text size="sm" strong truncate>
|
||||
{item.name}
|
||||
|
||||
@@ -17,7 +17,7 @@ type SectionCardProps = {
|
||||
|
||||
export function SectionCard({ title, count, barColor, onMore, children }: SectionCardProps) {
|
||||
return (
|
||||
<Card padding="lg" className="section-card">
|
||||
<Card padding="md" className="section-card">
|
||||
<div className="section-header">
|
||||
<Space gap="sm">
|
||||
<span className="section-bar" style={{ backgroundColor: barColor }} aria-hidden="true" />
|
||||
|
||||
Reference in New Issue
Block a user