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,8 +1,9 @@
import { Space, Tag, Text } from '@stash/ui'
import type { ReactNode } from 'react'
/**
* 顶栏中间的一条信息,形如「[标签] 文本」。
* 标签带浅色底,用于区分「本地 / 状态」这类类型前缀。
* 标签用组件库的 Tag(success / primary 两种语气)。
*/
type HeaderInfoItemProps = {
/** 可选的前缀标签 */
@@ -14,11 +15,15 @@ type HeaderInfoItemProps = {
export function HeaderInfoItem({ tag, tone = 'blue', children }: HeaderInfoItemProps) {
return (
<div className="header-info-item">
<Space gap="sm">
{tag !== undefined && (
<span className={`header-info-tag${tone === 'green' ? ' is-green' : ''}`}>{tag}</span>
<Tag tone={tone === 'green' ? 'success' : 'primary'} shape="rounded">
{tag}
</Tag>
)}
<span className="header-info-text">{children}</span>
</div>
<Text size="sm" tone="sub">
{children}
</Text>
</Space>
)
}
+28 -37
View File
@@ -1,10 +1,9 @@
import { SearchIcon, UserIcon } from '../common/Icon'
import { Icon, IconButton, Space } from '@stash/ui'
import { HeaderInfoItem } from './HeaderInfoItem'
/**
* 顶部栏:左品牌、中间运行信息、右侧操作图标。
* 信息区展示「共 N 个本地应用 / 第一个项目 / 连接状态」,
* 对应模板里的 header-center-info。
* 外层 <header> 由 Layout 提供,这里只渲染栏内的内容。
*/
type SiteHeaderProps = {
projectCount: number
@@ -16,43 +15,35 @@ type SiteHeaderProps = {
export function SiteHeader({ projectCount, sampleProject, live, onFocusSearch }: SiteHeaderProps) {
return (
<header className="site-header">
<div className="layout-container site-header-inner">
<a className="site-brand" href="/" aria-label="Stash 首页">
<span className="site-brand-mark" aria-hidden="true">
S
</span>
<span className="site-brand-name">STASH</span>
</a>
<div className="site-header-inner">
<a className="site-brand" href="/" aria-label="Stash 首页">
<span className="site-brand-mark" aria-hidden="true">
S
</span>
<span className="site-brand-name">STASH</span>
</a>
<div className="header-info">
<HeaderInfoItem>共 {projectCount} 个本地应用</HeaderInfoItem>
<Space gap="lg" className="header-info">
<HeaderInfoItem>共 {projectCount} 个本地应用</HeaderInfoItem>
{sampleProject !== undefined && (
<HeaderInfoItem tag="本地">{sampleProject}</HeaderInfoItem>
)}
{sampleProject !== undefined && (
<HeaderInfoItem tag="本地">{sampleProject}</HeaderInfoItem>
)}
<HeaderInfoItem tag="状态" tone={live ? 'green' : 'blue'}>
{live ? '实时同步已连接' : '等待服务连接'}
</HeaderInfoItem>
</div>
<HeaderInfoItem tag="状态" tone={live ? 'green' : 'blue'}>
{live ? '实时同步已连接' : '等待服务连接'}
</HeaderInfoItem>
</Space>
<div className="header-actions">
<button
type="button"
className="header-icon-button"
title="搜索(Ctrl+K)"
aria-label="聚焦搜索框"
onClick={onFocusSearch}
>
<SearchIcon />
</button>
{/* TODO(auth): 后端登录接口就绪后接上,目前仅作入口占位 */}
<button type="button" className="header-icon-button" title="登录" aria-label="登录">
<UserIcon />
</button>
</div>
</div>
</header>
<Space gap="sm" className="header-actions">
<IconButton
label="聚焦搜索框(Ctrl+K)"
icon={<Icon name="search_3_line" />}
onClick={onFocusSearch}
/>
{/* TODO(auth): 后端登录接口就绪后接上,目前仅作入口占位 */}
<IconButton label="登录" icon={<Icon name="user_3_line" />} />
</Space>
</div>
)
}