收口前端平台组件库能力

新增 PlatformUiKit 通用弹窗、按钮、状态、空态、媒体、表单和标签等公共组件
迁移结果页、创作工作台、认证入口、RPG 暗色面板和运行态弹窗的重复 UI chrome
补充组件测试、页面回归测试、技术文档和 Hermes 共享决策记录
This commit is contained in:
2026-06-10 10:24:18 +08:00
parent a4ee6ff698
commit 1ad25e30f8
226 changed files with 23364 additions and 7825 deletions

View File

@@ -0,0 +1,126 @@
import type { ReactNode } from 'react';
import { PlatformMediaFrame } from './PlatformMediaFrame';
type PlatformMediaTileGridColumns = 'five' | 'six';
type PlatformMediaTileGridGap = 'xs' | 'sm';
type PlatformMediaTileGridAspect = 'auto' | 'square';
type PlatformMediaTileSurface = 'white' | 'slate' | 'bare';
type PlatformMediaTileGridSurface = 'none' | 'soft';
export type PlatformMediaTileGridItem = {
id: string;
src?: string | null;
alt?: string;
refreshKey?: string | number | null;
fallbackLabel?: string;
fallbackContent?: ReactNode;
testId?: string;
className?: string;
imageClassName?: string;
fallbackClassName?: string;
};
type PlatformMediaTileGridProps = {
items: PlatformMediaTileGridItem[];
columns?: PlatformMediaTileGridColumns;
gap?: PlatformMediaTileGridGap;
aspect?: PlatformMediaTileGridAspect;
surface?: PlatformMediaTileGridSurface;
tileSurface?: PlatformMediaTileSurface;
fallbackLabel?: string;
imageClassName?: string;
fallbackClassName?: string;
className?: string;
tileClassName?: string;
};
const PLATFORM_MEDIA_TILE_GRID_COLUMNS_CLASS: Record<
PlatformMediaTileGridColumns,
string
> = {
five: 'grid-cols-5',
six: 'grid-cols-6',
};
const PLATFORM_MEDIA_TILE_GRID_GAP_CLASS: Record<
PlatformMediaTileGridGap,
string
> = {
sm: 'gap-1.5',
xs: 'gap-1',
};
const PLATFORM_MEDIA_TILE_GRID_SURFACE_CLASS: Record<
PlatformMediaTileGridSurface,
string
> = {
none: '',
soft: 'bg-white/78 p-2',
};
const PLATFORM_MEDIA_TILE_SURFACE_CLASS: Record<
PlatformMediaTileSurface,
string
> = {
bare: 'border border-white/80',
slate: 'border border-white/80 bg-slate-50',
white: 'border border-white/80 bg-white shadow-sm',
};
/**
* 平台媒体缩略格网格。
* 统一承接结果页里同尺寸素材 tile 的网格、圆角、边框和图片/fallback 框。
*/
export function PlatformMediaTileGrid({
items,
columns = 'six',
gap = 'sm',
aspect = 'auto',
surface = 'none',
tileSurface = 'white',
fallbackLabel = '素材',
imageClassName = 'h-full w-full object-cover',
fallbackClassName = 'tracking-normal text-[var(--platform-text-soft)]',
className,
tileClassName,
}: PlatformMediaTileGridProps) {
return (
<div
className={[
'platform-media-tile-grid grid',
PLATFORM_MEDIA_TILE_GRID_COLUMNS_CLASS[columns],
PLATFORM_MEDIA_TILE_GRID_GAP_CLASS[gap],
PLATFORM_MEDIA_TILE_GRID_SURFACE_CLASS[surface],
aspect === 'square' ? 'aspect-[1/1]' : '',
className,
]
.filter(Boolean)
.join(' ')}
>
{items.map((item) => (
<PlatformMediaFrame
key={item.id}
src={item.src}
alt={item.alt ?? ''}
refreshKey={item.refreshKey}
fallbackLabel={item.fallbackLabel ?? fallbackLabel}
fallbackContent={item.fallbackContent}
aspect="square"
surface="none"
data-testid={item.testId}
imageClassName={item.imageClassName ?? imageClassName}
fallbackClassName={item.fallbackClassName ?? fallbackClassName}
className={[
'platform-media-tile-grid__item min-h-0 rounded-[0.45rem]',
PLATFORM_MEDIA_TILE_SURFACE_CLASS[tileSurface],
tileClassName,
item.className,
]
.filter(Boolean)
.join(' ')}
/>
))}
</div>
);
}