收口前端平台组件库能力

新增 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

@@ -16,7 +16,9 @@ import { useEffect, useMemo, useState } from 'react';
import type { PublicUserSummary } from '../../../packages/shared/src/contracts/auth';
import { buildPublicWorkDetailUrl } from '../../routing/appPageRoutes';
import { copyTextToClipboard } from '../../services/clipboard';
import { CopyCodeButton } from '../common/CopyCodeButton';
import { CopyFeedbackMessage } from '../common/CopyFeedbackMessage';
import { useCopyFeedback } from '../common/useCopyFeedback';
import { ResolvedAssetImage } from '../ResolvedAssetImage';
import {
buildPlatformWorldDisplayTags,
@@ -30,8 +32,8 @@ import {
isPuzzleClearGalleryEntry,
isWoodenFishGalleryEntry,
type PlatformPublicGalleryCard,
resolvePlatformWorkAuthorDisplayName,
resolvePlatformPublicWorkCode,
resolvePlatformWorkAuthorDisplayName,
resolvePlatformWorldCoverSlides,
resolvePlatformWorldStats,
} from '../rpg-entry/rpgEntryWorldPresentation';
@@ -130,12 +132,16 @@ export function PlatformWorkDetailView({
entry,
authorSummary,
);
const [copyState, setCopyState] = useState<'idle' | 'copied' | 'failed'>(
'idle',
);
const [shareState, setShareState] = useState<'idle' | 'copied' | 'failed'>(
'idle',
);
const {
copyState,
copyText: copyWorkCodeText,
resetCopyState: resetWorkCodeCopyState,
} = useCopyFeedback();
const {
copyState: shareState,
copyText: copyShareText,
resetCopyState: resetShareCopyState,
} = useCopyFeedback();
const displayName = formatPlatformWorkDisplayName(entry.worldName);
const tags = useMemo(
() =>
@@ -181,7 +187,14 @@ export function PlatformWorkDetailView({
useEffect(() => {
setActiveCoverIndex(0);
}, [entry.profileId, coverSlides.length]);
resetWorkCodeCopyState();
resetShareCopyState();
}, [
entry.profileId,
coverSlides.length,
resetShareCopyState,
resetWorkCodeCopyState,
]);
useEffect(() => {
setActiveCoverIndex((current) =>
@@ -224,10 +237,7 @@ export function PlatformWorkDetailView({
return;
}
void copyTextToClipboard(publicWorkCode).then((copied) => {
setCopyState(copied ? 'copied' : 'failed');
window.setTimeout(() => setCopyState('idle'), 1400);
});
void copyWorkCodeText(publicWorkCode);
};
const sharePublicWork = () => {
@@ -236,10 +246,7 @@ export function PlatformWorkDetailView({
}
const shareText = `邀请你来玩《${entry.worldName}\n作品号${publicWorkCode}\n${buildPublicWorkDetailUrl(publicWorkCode)}`;
void copyTextToClipboard(shareText).then((copied) => {
setShareState(copied ? 'copied' : 'failed');
window.setTimeout(() => setShareState('idle'), 1400);
});
void copyShareText(shareText);
};
return (
@@ -432,23 +439,24 @@ export function PlatformWorkDetailView({
</div>
<p className="platform-work-detail__copy">{entry.summaryText}</p>
{publicWorkCode ? (
<button
type="button"
<CopyCodeButton
state={copyState}
code={publicWorkCode}
codeLabel={null}
accessibleLabel={publicWorkCode}
title="复制作品号"
className="platform-work-detail__code"
onClick={copyPublicWorkCode}
>
<Copy className="h-4 w-4" />
<span>{publicWorkCode}</span>
{copyState !== 'idle' ? (
<span>{copyState === 'copied' ? '已复制' : '复制失败'}</span>
) : null}
</button>
) : null}
{shareState !== 'idle' ? (
<div className="platform-work-detail__toast">
{shareState === 'copied' ? '分享内容已复制' : '分享失败'}
</div>
idleIcon={<Copy className="h-4 w-4" />}
copiedIcon={<Copy className="h-4 w-4" />}
/>
) : null}
<CopyFeedbackMessage
state={shareState}
className="platform-work-detail__toast"
copiedLabel="分享内容已复制"
failedLabel="分享失败"
/>
</section>
</div>