继续收口RPG发布检查弹窗壳层

RPG结果页发布检查弹窗复用 PlatformToolModalShell

保留发布检查、封面预览和发布动作业务语义

补充组件测试断言共享白底工具弹窗壳层

同步 PlatformUiKit 文档和 Hermes 决策记录
This commit is contained in:
2026-06-11 16:51:25 +08:00
parent ffcffef6d2
commit a20d7b1655
4 changed files with 81 additions and 109 deletions

View File

@@ -75,6 +75,8 @@ test('RPG result publish dialog uses PlatformPillBadge and PlatformSubpanel for
const coverBadge = within(dialog).getByText('上传封面');
const coverShell = within(dialog).getByLabelText('封面预览外壳');
expect(dialog.className).toContain('platform-remap-surface');
expect(dialog.className).toContain('shadow-[0_24px_80px_rgba(0,0,0,0.55)]');
expect(coverBadge.className).toContain('rounded-full');
expect(coverBadge.className).toContain('bg-white/72');
expect(coverShell.className).toContain('platform-subpanel');

View File

@@ -1,15 +1,13 @@
import { type ReactNode, useState } from 'react';
import { createPortal } from 'react-dom';
import { resolveCustomWorldCoverPresentation } from '../../services/customWorldCover';
import type { CustomWorldProfile } from '../../types';
import { useAuthUi } from '../auth/AuthUiContext';
import { PlatformActionButton } from '../common/PlatformActionButton';
import { PlatformFieldLabel } from '../common/PlatformFieldLabel';
import { PlatformModalCloseButton } from '../common/PlatformModalCloseButton';
import { PlatformPillBadge } from '../common/PlatformPillBadge';
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
import { PlatformSubpanel } from '../common/PlatformSubpanel';
import { PlatformToolModalShell } from '../common/PlatformToolModalShell';
import { CustomWorldCoverArtwork } from '../CustomWorldCoverArtwork';
function SmallButton({
@@ -54,112 +52,21 @@ function PublishPanelDialog({
onEditCover: () => void;
onPublish: () => void;
}) {
const platformTheme = useAuthUi()?.platformTheme ?? 'light';
const coverPresentation = resolveCustomWorldCoverPresentation(profile);
if (typeof document === 'undefined') {
return null;
}
return createPortal(
<div
className={`platform-theme platform-theme--${platformTheme} platform-overlay fixed inset-0 z-[140] flex items-end justify-center p-3 backdrop-blur-sm sm:items-center sm:p-4`}
onClick={(event) => {
if (event.target === event.currentTarget) {
onClose();
}
}}
>
<div
role="dialog"
aria-modal="true"
aria-label="发布作品"
className="platform-modal-shell platform-remap-surface flex max-h-[min(90vh,46rem)] w-full max-w-4xl flex-col overflow-hidden rounded-t-[1.75rem] shadow-[0_24px_80px_rgba(0,0,0,0.55)] sm:rounded-[1.75rem]"
onClick={(event) => event.stopPropagation()}
>
<div className="flex items-center justify-between gap-3 border-b border-[var(--platform-subpanel-border)] px-5 py-4">
<div>
<div className="text-base font-semibold text-[var(--platform-text-strong)]">
</div>
<div className="mt-1 text-sm leading-6 text-[var(--platform-text-soft)]">
</div>
</div>
<PlatformModalCloseButton
variant="platformIcon"
label="关闭发布作品"
onClick={onClose}
/>
</div>
<div className="min-h-0 flex-1 overflow-y-auto px-5 py-4">
<div className="grid gap-4 lg:grid-cols-[minmax(0,1fr)_minmax(18rem,0.78fr)]">
<div className="space-y-3">
<PlatformFieldLabel variant="section">
</PlatformFieldLabel>
{blockers.length > 0 ? (
<div className="space-y-2">
{blockers.map((blocker, index) => (
<PlatformStatusMessage
key={`publish-blocker-${index}-${blocker}`}
tone="warning"
surface="platform"
>
<div className="text-xs font-semibold tracking-[0.14em] text-[var(--platform-warm-text)] opacity-80">
{index + 1}
</div>
<div className="mt-1 text-[var(--platform-text-strong)]">
{blocker}
</div>
</PlatformStatusMessage>
))}
</div>
) : (
<PlatformStatusMessage tone="success" surface="platform">
</PlatformStatusMessage>
)}
</div>
<div className="space-y-3">
<div className="flex items-center justify-between gap-3">
<PlatformFieldLabel variant="section">
</PlatformFieldLabel>
<PlatformPillBadge
tone="neutral"
size="xs"
className="px-2.5 py-1 text-[10px]"
>
{coverPresentation.sourceType === 'uploaded'
? '上传封面'
: coverPresentation.sourceType === 'generated'
? 'AI封面'
: '默认封面'}
</PlatformPillBadge>
</div>
<PlatformSubpanel
padding="none"
aria-label="封面预览外壳"
className="p-2"
>
<CustomWorldCoverArtwork
imageSrc={coverPresentation.imageSrc}
title={profile.name}
fallbackLabel={profile.name.slice(0, 4) || '封面'}
renderMode={coverPresentation.renderMode}
characterImageSrcs={coverPresentation.characterImageSrcs}
className="aspect-[16/9] max-h-[15rem] rounded-[1rem]"
/>
</PlatformSubpanel>
<SmallButton onClick={onEditCover} tone="sky">
</SmallButton>
</div>
</div>
</div>
<div className="flex flex-col-reverse gap-3 border-t border-[var(--platform-subpanel-border)] px-5 py-4 sm:flex-row sm:justify-end">
return (
<PlatformToolModalShell
open
title="发布作品"
description="发布前检查与封面设置"
onClose={onClose}
closeLabel="关闭发布作品"
zIndexClassName="z-[140]"
size="xl"
panelClassName="!max-h-[min(90vh,46rem)] !max-w-4xl"
footerClassName="flex-col-reverse sm:flex-row sm:justify-end"
footer={
<>
<SmallButton onClick={onClose}></SmallButton>
<PlatformActionButton
onClick={onPublish}
@@ -167,10 +74,71 @@ function PublishPanelDialog({
>
{isPublishing ? '发布中...' : '发布到广场'}
</PlatformActionButton>
</>
}
>
<div className="grid gap-4 lg:grid-cols-[minmax(0,1fr)_minmax(18rem,0.78fr)]">
<div className="space-y-3">
<PlatformFieldLabel variant="section"></PlatformFieldLabel>
{blockers.length > 0 ? (
<div className="space-y-2">
{blockers.map((blocker, index) => (
<PlatformStatusMessage
key={`publish-blocker-${index}-${blocker}`}
tone="warning"
surface="platform"
>
<div className="text-xs font-semibold tracking-[0.14em] text-[var(--platform-warm-text)] opacity-80">
{index + 1}
</div>
<div className="mt-1 text-[var(--platform-text-strong)]">
{blocker}
</div>
</PlatformStatusMessage>
))}
</div>
) : (
<PlatformStatusMessage tone="success" surface="platform">
</PlatformStatusMessage>
)}
</div>
<div className="space-y-3">
<div className="flex items-center justify-between gap-3">
<PlatformFieldLabel variant="section"></PlatformFieldLabel>
<PlatformPillBadge
tone="neutral"
size="xs"
className="px-2.5 py-1 text-[10px]"
>
{coverPresentation.sourceType === 'uploaded'
? '上传封面'
: coverPresentation.sourceType === 'generated'
? 'AI封面'
: '默认封面'}
</PlatformPillBadge>
</div>
<PlatformSubpanel
padding="none"
aria-label="封面预览外壳"
className="p-2"
>
<CustomWorldCoverArtwork
imageSrc={coverPresentation.imageSrc}
title={profile.name}
fallbackLabel={profile.name.slice(0, 4) || '封面'}
renderMode={coverPresentation.renderMode}
characterImageSrcs={coverPresentation.characterImageSrcs}
className="aspect-[16/9] max-h-[15rem] rounded-[1rem]"
/>
</PlatformSubpanel>
<SmallButton onClick={onEditCover} tone="sky">
</SmallButton>
</div>
</div>
</div>,
document.body,
</PlatformToolModalShell>
);
}