refactor(web): 前端改用 @stash/ui 组件库

- 新增本地依赖 @stash/ui(file:../../stash-ui),引入其 style.css 与设计令牌
- 页面骨架改用 Layout(contained + header/sider),搜索框改为吸顶
- 卡片/菜单/按钮/标签/头像/分页/空态/提示全部换成组件库组件
- 删除本地 iconfont 与 1400 行手写组件样式,App.css 只保留页面级样式
- 数据层、路由、过滤逻辑未改动;33 个 Rust 测试通过
This commit is contained in:
stash
2026-10-28 12:44:50 +08:00
parent 19ead7b716
commit 64f1fbf906
35 changed files with 735 additions and 2154 deletions
@@ -1,6 +1,6 @@
import { Button, Card, Icon, IconButton, Paragraph, Space, Tag, Text, Title } from '@stash/ui'
import type { ItemDetailModel } from '../../data/detail'
import type { RelatedItem } from '../../data/types'
import { ExternalIcon, EyeIcon, ShareIcon, ThumbUpIcon } from '../common/Icon'
import { RelatedList } from './RelatedList'
/**
@@ -27,20 +27,24 @@ export function ItemDetailView({
}: ItemDetailViewProps) {
return (
<div className="detail-view item-view">
<div className="detail-nav card-box">
<button type="button" className="detail-back" onClick={onBack}>
返回
</button>
<span className="detail-breadcrumb">{breadcrumb}</span>
</div>
<Card padding="sm">
<Space justify="between" wrap>
<Button variant="ghost" size="sm" onClick={onBack}>
返回
</Button>
<Text size="sm" tone="sub">
{breadcrumb}
</Text>
</Space>
</Card>
<article className="item-card card-box">
<h2 className="item-title">
<Card padding="lg">
<Title level={2}>
{model.title}
<button type="button" className="item-edit" disabled title="编辑功能待接入">
[编辑]
</button>
</h2>
<Button variant="ghost" size="sm" disabled className="item-edit">
编辑
</Button>
</Title>
<div className={`item-cover item-cover--${model.shape}`}>
{model.cover !== undefined ? (
@@ -51,68 +55,62 @@ export function ItemDetailView({
</div>
{model.tags.length > 0 && (
<div className="item-tags">
<Space gap="sm" wrap>
{model.tags.map((tag) => (
<span className="item-tag" key={tag}>
<Tag key={tag} shape="rounded">
{tag}
</span>
</Tag>
))}
</div>
</Space>
)}
{model.comment !== undefined && (
<div className="item-quote">
<span className="item-quote-mark" aria-hidden="true">
“
</span>
<p className="item-quote-text">{model.comment}</p>
</div>
<Paragraph tone="secondary" className="item-quote">
{model.comment}
</Paragraph>
)}
{model.paragraphs.map((paragraph, index) => (
<p className="item-desc" key={`${index}-${paragraph.slice(0, 8)}`}>
<Paragraph key={`${index}-${paragraph.slice(0, 8)}`} tone="main">
{paragraph}
</p>
</Paragraph>
))}
<div className="item-actions">
<span className="item-stat" title="浏览量">
<EyeIcon />
<em>{model.views}</em>
</span>
<span className="item-stat" title="点赞数">
<ThumbUpIcon />
<em>{model.likes}</em>
</span>
<button
type="button"
className="item-stat item-stat--icon"
title="分享(待接入)"
aria-label="分享"
>
<ShareIcon />
</button>
<Space justify="between" wrap className="item-actions">
<Space gap="lg">
<Space gap="xs" className="item-stat">
<Icon name="eye_line" />
<Text size="sm" tone="sub">
{model.views}
</Text>
</Space>
<Space gap="xs" className="item-stat">
<Icon name="thumb_up_2_line" />
<Text size="sm" tone="sub">
{model.likes}
</Text>
</Space>
<IconButton label="分享(待接入)" size="xs" icon={<Icon name="share_3_line" />} />
</Space>
{/* 占位数据没有真实地址时,按钮置灰而不是打开空白页 */}
{model.href === '#' ? (
<button type="button" className="item-jump" disabled title="暂无跳转地址">
<Button size="sm" disabled>
跳转到
<ExternalIcon />
</button>
<Icon name="a-external_link_line1" />
</Button>
) : (
<a className="item-jump" href={model.href} target="_blank" rel="noreferrer">
<a className="link-button" href={model.href} target="_blank" rel="noreferrer">
跳转到
<ExternalIcon />
<Icon name="a-external_link_line1" />
</a>
)}
</div>
</Space>
{model.banner !== undefined && (
<img className="item-banner" src={model.banner} alt="" loading="lazy" />
)}
</article>
</Card>
<RelatedList items={related} onOpen={onOpenItem} />
</div>