收口单按钮已读状态弹窗

新增 PlatformAcknowledgeStatusDialog 统一承接单按钮已读状态壳层
迁移大鱼结果页与个人中心支付结果复用共享已读状态弹窗
迁移 RPG 编辑器与平台入口提示链路复用共享已读状态弹窗
迁移自定义世界实体目录阻断提示复用共享已读状态弹窗
补充共享组件测试并更新 PlatformUiKit 收口文档与决策记录
This commit is contained in:
2026-06-11 00:38:26 +08:00
parent 6163350f5c
commit edf37d97a7
9 changed files with 181 additions and 49 deletions

View File

@@ -41,6 +41,7 @@
- 2026-06-10 追加:`RpgCreationRoleAssetStudioModalImpl.tsx` 的角色形象生成 / 动作草稿生成确认也并入 `PlatformMudPointConfirmDialog`;共享组件通过自定义 title 与补充说明承接工坊语义,工坊页不再单独维护 `UnifiedConfirmDialog` 的标准泥点文案骨架。后续同类“确认消耗泥点 + 补充说明”场景继续优先复用该 Module。
- 2026-06-10 追加:平台危险确认统一收口到 `src/components/common/PlatformDangerConfirmDialog.tsx`;该 Module 专门承接“确认 / 取消 + 危险主动作”的标准骨架,当前已覆盖 `PlatformEntryFlowShellImpl.tsx` 的删除作品确认、`RpgCreationResultViewImpl.tsx` 的重新生成确认和 `CustomWorldEntityCatalog.tsx` 的删除角色 / 批量删除确认。后续删除、覆盖、清空等危险动作优先复用该 Module不再在业务页重复拼接 `UnifiedConfirmDialog``showCancel + confirmTone=\"danger\"` 组合。
- 2026-06-10 追加:平台未保存离开确认统一收口到 `src/components/common/PlatformUnsavedLeaveConfirmDialog.tsx`;该 Module 专门承接“继续编辑 + 确认离开”的标准骨架,当前已覆盖 `RpgCreationEntityEditorShared.tsx` 里的关闭未保存修改、生成结果未保存退出和普通结果未保存退出确认。后续同类未保存离开场景优先复用该 Module不再在业务页重复拼接 `UnifiedConfirmDialog``showCancel + cancelLabel=\"继续编辑\"` 组合和重复壳层 class。
- 2026-06-10 追加:平台单按钮已读状态统一收口到 `src/components/common/PlatformAcknowledgeStatusDialog.tsx`;该 Module 专门承接“状态提示 + 知道了”的单按钮确认已读语义,当前已覆盖 `BigFishResultView.tsx` 的发布失败提示、`RpgEntryHomeView.tsx` 的支付结果提示、`RpgCreationEntityEditorShared.tsx` 的编辑器 notice、`PlatformEntryFlowShellImpl.tsx` 的泥点提示 / 作品不可用 / 搜索未命中提示,以及 `CustomWorldEntityCatalog.tsx` 的“无法删除”阻断提示。后续同类 status-dialog 场景优先复用该 Module不再在业务页重复拼装 `action={{ label: '知道了', onClick: onClose }}`
- 2026-06-10 追加RPG 首页个人中心里的统计卡、统计骨架、常用功能入口、设置行和法律信息入口统一抽到 `src/components/platform-entry/PlatformProfilePrimitives.tsx`;这组纯展示原子以后优先通过 props 接收图片资源、点击回调和展示文案,不再继续塞回 `RpgEntryHomeView` 的账户控制逻辑里。新建 `PlatformProfilePrimitives.test.tsx` 作为组件级护栏,页面级布局与法律入口继续由 `RpgEntryHomeView.recharge.test.tsx` 兜底。
- 2026-06-10 追加RPG 首页个人中心的充值 / 钱包 / 每日任务 / 邀请 / 兑换码等商业与账户控制逻辑统一收口到 `src/components/platform-entry/usePlatformProfileCenterController.ts`controller 负责账户动作分流、商业状态派生与相关面板控制,`RpgEntryHomeView` 只保留展示、昵称头像编辑、扫码入口和页面级交互编排,不在页面组件里继续堆叠账户控制分支。验证命令:`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx``npm run typecheck`
- 2026-06-10 追加RPG 首页个人中心的“玩过 / 可继续”历史弹层统一抽到 `src/components/platform-entry/PlatformProfilePlayedWorksModal.tsx``RpgEntryHomeView` 不再内联 `SaveArchiveCard``ProfilePlayedWorksModal` 和未连通的 `ProfileSaveArchivesModal`。当前产品语义已经把存档恢复并入“玩过”弹层的“可继续”分区,因此 controller 里的 `ProfilePopupPanel` 也去掉了没有真实入口的 `saveArchives` 分支。验证命令:`npm run test -- src/components/platform-entry/PlatformProfilePlayedWorksModal.test.tsx src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx``npm run typecheck`

View File

@@ -246,6 +246,7 @@
19.3.22. 标准泥点消耗确认弹窗收口到 `src/components/common/PlatformMudPointConfirmDialog.tsx`;组件固定承接“确认消耗泥点 + 消耗 N 泥点”的同形态标题、正文骨架和确认 / 取消动作,业务页只保留点数、补充说明和确认回调。`PuzzleCreationWorkspace.tsx``Match3DCreationWorkspace.tsx``PuzzleResultView.tsx``Match3DResultView.tsx` 以及 `RpgCreationRoleAssetStudioModalImpl.tsx` 已迁移;其中角色形象生成 / 动作草稿生成继续通过自定义 title 和补充说明承接工坊语义,但不再各自拼接 `UnifiedConfirmDialog` 的相同文案和内容结构。后续同类泥点确认优先复用该 Module像 runtime 道具确认、预计消耗区间确认这类节奏不同的弹层再单独评估是否扩展变体。
19.3.23. 平台危险确认弹窗收口到 `src/components/common/PlatformDangerConfirmDialog.tsx`;组件固定承接“确认 / 取消 + 危险主动作”的标准骨架,并透传忙碌态、遮罩关闭策略、按钮文案和局部面板样式。`PlatformEntryFlowShellImpl.tsx` 的删除作品确认、`RpgCreationResultViewImpl.tsx` 的重新生成确认,以及 `CustomWorldEntityCatalog.tsx` 的删除角色 / 批量删除确认已迁移;业务页继续保留标题、说明文案和确认回调,不再各自拼接 `UnifiedConfirmDialog` 的危险按钮配置。后续删除、覆盖、清空等危险动作优先复用该 Module再按需要补充更窄的语义 wrapper。
19.3.24. 平台未保存离开确认弹窗收口到 `src/components/common/PlatformUnsavedLeaveConfirmDialog.tsx`;组件固定承接“继续编辑 + 确认离开”的标准骨架,并按 `platform / pixel` 两类确认风格兜底默认遮罩和面板样式。`RpgCreationEntityEditorShared.tsx` 中的关闭未保存修改确认、生成结果未保存退出确认和普通结果未保存退出确认已迁移;业务页只保留标题、确认按钮文案和未保存提示内容,不再各自拼接 `UnifiedConfirmDialog` 的 cancel/confirm 组合和重复壳层 class。
19.3.25. 平台单按钮已读状态弹窗收口到 `src/components/common/PlatformAcknowledgeStatusDialog.tsx`;组件固定承接“状态提示 + 知道了”这一类单按钮确认已读语义,并透传 action surface / size / fullWidth / class、header、关闭路径和局部 panel 覆写。`BigFishResultView.tsx` 的发布失败提示、`RpgEntryHomeView.tsx` 的支付结果提示、`RpgCreationEntityEditorShared.tsx` 的编辑器 notice、`PlatformEntryFlowShellImpl.tsx` 的泥点提示 / 作品不可用 / 搜索未命中提示,以及 `CustomWorldEntityCatalog.tsx` 的“无法删除”阻断提示已迁移;业务页继续保留 status、标题、说明和关闭回调不再各自手写 `PlatformStatusDialog``action={{ label: '知道了', onClick: onClose }}` 结构。
19.3. creative-agent 首页的侧边栏菜单、账号入口、开启新对话、我的创作、首页激励 CTA 和 prompt suggestion 按钮迁移到 `PlatformIconButton` / `PlatformActionButton`;首页继续保留 `creative-agent-home__*` 本地 class 承接透明顶栏、抽屉和品牌化胶囊视觉,不把视觉回收和语义收口绑成一次大改。`Beta` 徽标和历史记录纯文本行暂保留本地实现,等出现更多同构轻量列表行后再评估是否抽新的共享 row primitive。
19.4. 大鱼吃小鱼结果页 hero 的返回入口迁移到 `PlatformIconButton variant="darkMini"`,测试 / 发布动作迁移到 `PlatformActionButton surface="editorDark"`;结果页只保留测试运行、发布提交和文案状态语义,不再手写 hero 顶栏按钮壳。
19.4.1. 大鱼吃小鱼结果页的发布失败弹层迁移到 `src/components/common/PlatformStatusDialog.tsx``PlatformStatusDialog` 补充自定义图标、可访问标签和动作按钮样式透传后,`BigFishResultView` 不再保留 `BigFishResultErrorModal` 内联的 `UnifiedConfirmDialog + PlatformIconBadge` 组合。结果页只保留失败文案和关闭回调,发布失败的状态图标、遮罩、白底面板和“知道了”主动作统一由共享状态弹层承接。验证命令:`npm run test -- src/components/common/PlatformStatusDialog.test.tsx src/components/big-fish-result/BigFishResultView.test.tsx``npm run typecheck`

View File

@@ -23,6 +23,7 @@ import {
type SceneChapterBlueprint,
} from '../types';
import { CharacterAnimator } from './CharacterAnimator';
import { PlatformAcknowledgeStatusDialog } from './common/PlatformAcknowledgeStatusDialog';
import { PlatformActionButton } from './common/PlatformActionButton';
import { PlatformDangerConfirmDialog } from './common/PlatformDangerConfirmDialog';
import { PlatformEmptyState } from './common/PlatformEmptyState';
@@ -30,7 +31,6 @@ import { PlatformMediaFrame } from './common/PlatformMediaFrame';
import { PlatformPillBadge } from './common/PlatformPillBadge';
import { PlatformProgressBar } from './common/PlatformProgressBar';
import { PlatformStatGrid } from './common/PlatformStatGrid';
import { PlatformStatusDialog } from './common/PlatformStatusDialog';
import { PlatformStatusMessage } from './common/PlatformStatusMessage';
import { PlatformSubpanel } from './common/PlatformSubpanel';
import { PlatformTextField } from './common/PlatformTextField';
@@ -1440,17 +1440,12 @@ export function CustomWorldEntityCatalog({
</PlatformDangerConfirmDialog>
) : null}
{confirmState?.kind === 'minimum-playable' ? (
<PlatformStatusDialog
<PlatformAcknowledgeStatusDialog
status="error"
title="无法删除"
description="至少保留一个可扮演角色,才能正常进入自定义世界。"
onClose={closeConfirmDialog}
closeOnBackdrop={false}
action={{
label: '知道了',
onClick: closeConfirmDialog,
surface: 'platform',
}}
/>
) : null}
</div>

View File

@@ -16,6 +16,7 @@ import type {
BigFishSessionSnapshotResponse,
ExecuteBigFishActionRequest,
} from '../../../packages/shared/src/contracts/bigFish';
import { PlatformAcknowledgeStatusDialog } from '../common/PlatformAcknowledgeStatusDialog';
import { PlatformActionButton } from '../common/PlatformActionButton';
import { PlatformEmptyState } from '../common/PlatformEmptyState';
import { PlatformFieldLabel } from '../common/PlatformFieldLabel';
@@ -23,7 +24,6 @@ import { PlatformIconBadge } from '../common/PlatformIconBadge';
import { PlatformIconButton } from '../common/PlatformIconButton';
import { PlatformMediaFrame } from '../common/PlatformMediaFrame';
import { PlatformPillBadge } from '../common/PlatformPillBadge';
import { PlatformStatusDialog } from '../common/PlatformStatusDialog';
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
import { PlatformSubpanel } from '../common/PlatformSubpanel';
@@ -619,7 +619,7 @@ function BigFishResultErrorModal({
onClose: () => void;
}) {
return (
<PlatformStatusDialog
<PlatformAcknowledgeStatusDialog
status="error"
title="发布失败"
description={message}
@@ -627,12 +627,7 @@ function BigFishResultErrorModal({
icon={<Waves className="h-4 w-4" />}
iconLabel="发布失败提示"
iconClassName="mt-0.5 bg-[var(--platform-button-danger-fill)] text-[var(--platform-button-danger-text)]"
action={{
label: '知道了',
onClick: onClose,
surface: 'platform',
className: 'border-slate-950 bg-slate-950 text-white',
}}
actionClassName="border-slate-950 bg-slate-950 text-white"
zIndexClassName="z-[160]"
overlayClassName="bg-slate-950/58"
panelClassName="border-red-100/80 bg-white text-slate-950 shadow-2xl"

View File

@@ -0,0 +1,48 @@
/* @vitest-environment jsdom */
import { fireEvent, render, screen } from '@testing-library/react';
import { expect, test, vi } from 'vitest';
import { PlatformAcknowledgeStatusDialog } from './PlatformAcknowledgeStatusDialog';
test('renders a standard acknowledge action and closes through 知道了', () => {
const onClose = vi.fn();
render(
<PlatformAcknowledgeStatusDialog
status="error"
title="提示"
description="至少保留一个可扮演角色。"
onClose={onClose}
/>,
);
const action = screen.getByRole('button', { name: '知道了' });
expect(action.className).toContain('platform-button');
fireEvent.click(action);
expect(onClose).toHaveBeenCalledTimes(1);
});
test('supports custom action styling and header notice layout', () => {
render(
<PlatformAcknowledgeStatusDialog
status="error"
title="发布失败"
description="还缺少 16 个基础动作"
onClose={() => {}}
showHeader
showCloseButton
closeOnBackdrop
iconLabel="发布失败提示"
actionClassName="border-slate-950 bg-slate-950 text-white"
/>,
);
const action = screen.getByRole('button', { name: '知道了' });
const dialog = screen.getByRole('dialog', { name: '发布失败' });
expect(action.className).toContain('border-slate-950');
expect(action.className).toContain('bg-slate-950');
expect(dialog.querySelector('[aria-label="发布失败提示"]')).toBeTruthy();
});

View File

@@ -0,0 +1,111 @@
import type { ReactNode } from 'react';
import type {
PlatformActionButtonSize,
PlatformActionButtonSurface,
PlatformActionButtonTone,
} from './platformActionButtonModel';
import {
PlatformStatusDialog,
type PlatformStatusDialogStatus,
} from './PlatformStatusDialog';
type PlatformAcknowledgeStatusDialogProps = {
open?: boolean;
status: PlatformStatusDialogStatus;
title: string;
description?: ReactNode;
children?: ReactNode;
onClose: () => void;
actionLabel?: string;
actionDisabled?: boolean;
actionTone?: PlatformActionButtonTone;
actionSurface?: PlatformActionButtonSurface;
actionSize?: PlatformActionButtonSize;
actionFullWidth?: boolean;
actionClassName?: string;
showHeader?: boolean;
showBodyTitle?: boolean;
showCloseButton?: boolean;
closeOnBackdrop?: boolean;
closeOnEscape?: boolean;
closeLabel?: string;
closeDisabled?: boolean;
zIndexClassName?: string;
overlayClassName?: string;
panelClassName?: string;
bodyClassName?: string;
iconClassName?: string;
icon?: ReactNode;
iconLabel?: string;
};
/**
* 平台已读确认状态弹窗。
* 统一承接“状态提示 + 知道了”这一类单按钮确认已读的弹窗语义。
*/
export function PlatformAcknowledgeStatusDialog({
open,
status,
title,
description,
children,
onClose,
actionLabel = '知道了',
actionDisabled = false,
actionTone,
actionSurface = 'platform',
actionSize,
actionFullWidth,
actionClassName,
showHeader,
showBodyTitle,
showCloseButton,
closeOnBackdrop,
closeOnEscape,
closeLabel,
closeDisabled,
zIndexClassName,
overlayClassName,
panelClassName,
bodyClassName,
iconClassName,
icon,
iconLabel,
}: PlatformAcknowledgeStatusDialogProps) {
return (
<PlatformStatusDialog
open={open}
status={status}
title={title}
description={description}
onClose={onClose}
showHeader={showHeader}
showBodyTitle={showBodyTitle}
showCloseButton={showCloseButton}
closeOnBackdrop={closeOnBackdrop}
closeOnEscape={closeOnEscape}
closeLabel={closeLabel}
closeDisabled={closeDisabled}
zIndexClassName={zIndexClassName}
overlayClassName={overlayClassName}
panelClassName={panelClassName}
bodyClassName={bodyClassName}
iconClassName={iconClassName}
icon={icon}
iconLabel={iconLabel}
action={{
label: actionLabel,
onClick: onClose,
disabled: actionDisabled,
tone: actionTone,
surface: actionSurface,
size: actionSize,
fullWidth: actionFullWidth,
className: actionClassName,
}}
>
{children}
</PlatformStatusDialog>
);
}

View File

@@ -363,10 +363,10 @@ import {
} from '../../services/wooden-fish/woodenFishClient';
import type { CustomWorldProfile } from '../../types';
import { useAuthUi } from '../auth/AuthUiContext';
import { PlatformAcknowledgeStatusDialog } from '../common/PlatformAcknowledgeStatusDialog';
import { PlatformActionButton } from '../common/PlatformActionButton';
import { PlatformDangerConfirmDialog } from '../common/PlatformDangerConfirmDialog';
import { PlatformFieldLabel } from '../common/PlatformFieldLabel';
import { PlatformStatusDialog } from '../common/PlatformStatusDialog';
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
import { PlatformSubpanel } from '../common/PlatformSubpanel';
import { PublishShareModal } from '../common/PublishShareModal';
@@ -16997,7 +16997,7 @@ export function PlatformEntryFlowShellImpl({
}}
/>
) : null}
<PlatformStatusDialog
<PlatformAcknowledgeStatusDialog
open={Boolean(draftGenerationPointNotice)}
status="error"
title={draftGenerationPointNotice?.title ?? '泥点提示'}
@@ -17013,14 +17013,9 @@ export function PlatformEntryFlowShellImpl({
? undefined
: 'bg-amber-100/80 text-amber-600'
}
action={{
label: '知道了',
onClick: () => setDraftGenerationPointNotice(null),
surface: 'platform',
}}
>
{draftGenerationPointNotice?.message}
</PlatformStatusDialog>
</PlatformAcknowledgeStatusDialog>
<PublishShareModal
open={Boolean(publishSharePayload)}
payload={publishSharePayload}
@@ -17038,7 +17033,7 @@ export function PlatformEntryFlowShellImpl({
overlayClassName={`platform-theme ${platformThemeClass} !items-center`}
panelClassName="platform-remap-surface rounded-[1.5rem]"
/>
<PlatformStatusDialog
<PlatformAcknowledgeStatusDialog
open={Boolean(workNotFoundRecoveryDialog)}
status="error"
title="作品不可用"
@@ -17048,14 +17043,9 @@ export function PlatformEntryFlowShellImpl({
closeOnBackdrop
overlayClassName={`platform-theme ${platformThemeClass} !items-center`}
panelClassName="platform-remap-surface rounded-[1.75rem]"
action={{
label: '知道了',
onClick: confirmWorkNotFoundRecovery,
surface: 'platform',
}}
>
{workNotFoundRecoveryDialog?.message}
</PlatformStatusDialog>
</PlatformAcknowledgeStatusDialog>
<PlatformDangerConfirmDialog
open={Boolean(pendingDeleteCreationWork)}
title="删除作品"
@@ -17083,7 +17073,7 @@ export function PlatformEntryFlowShellImpl({
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<PlatformStatusDialog
<PlatformAcknowledgeStatusDialog
status="error"
title="未找到结果"
description="公开编号搜索"
@@ -17093,14 +17083,9 @@ export function PlatformEntryFlowShellImpl({
closeLabel="关闭搜索结果"
overlayClassName={`platform-theme ${platformThemeClass} !items-center bg-black/45 !p-4`}
panelClassName="platform-remap-surface rounded-[1.6rem]"
action={{
label: '知道了',
onClick: closePublicSearchResult,
surface: 'platform',
}}
>
{publicSearchError}
</PlatformStatusDialog>
</PlatformAcknowledgeStatusDialog>
</motion.div>
) : searchedPublicUser ? (
<motion.div

View File

@@ -94,6 +94,7 @@ import {
} from '../asset-studio/characterAssetWorkflowPersistence';
import { useAuthUi } from '../auth/AuthUiContext';
import { CharacterAnimator } from '../CharacterAnimator';
import { PlatformAcknowledgeStatusDialog } from '../common/PlatformAcknowledgeStatusDialog';
import { PlatformAssetPickerGrid } from '../common/PlatformAssetPickerCard';
import { PlatformEmptyState } from '../common/PlatformEmptyState';
import { PlatformIconButton } from '../common/PlatformIconButton';
@@ -102,7 +103,6 @@ import { PlatformModalCloseButton } from '../common/PlatformModalCloseButton';
import { PlatformOverlayBadge } from '../common/PlatformOverlayBadge';
import { PlatformPillBadge } from '../common/PlatformPillBadge';
import { PlatformSlotBadge } from '../common/PlatformSlotBadge';
import { PlatformStatusDialog } from '../common/PlatformStatusDialog';
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
import { PlatformSubpanel } from '../common/PlatformSubpanel';
import { PlatformUnsavedLeaveConfirmDialog } from '../common/PlatformUnsavedLeaveConfirmDialog';
@@ -1491,7 +1491,7 @@ function EditorNoticeDialog({
onClose: () => void;
}) {
return (
<PlatformStatusDialog
<PlatformAcknowledgeStatusDialog
status="error"
title="提示"
description={message}
@@ -1500,14 +1500,9 @@ function EditorNoticeDialog({
showCloseButton
closeOnBackdrop
closeOnEscape
action={{
label: '知道了',
onClick: onClose,
surface: 'platform',
size: 'sm',
fullWidth: false,
className: 'min-h-0 rounded-full px-4 py-2',
}}
actionSize="sm"
actionFullWidth={false}
actionClassName="min-h-0 rounded-full px-4 py-2"
zIndexClassName="z-[140]"
overlayClassName="rpg-editor-notice-dialog-overlay !items-center"
panelClassName="platform-remap-surface rounded-[1.5rem]"

View File

@@ -73,6 +73,7 @@ import {
import { shouldShowRechargeEntry } from '../../services/payment/paymentPlatform';
import type { CustomWorldProfile } from '../../types';
import { useAuthUi } from '../auth/AuthUiContext';
import { PlatformAcknowledgeStatusDialog } from '../common/PlatformAcknowledgeStatusDialog';
import { CopyFeedbackButton } from '../common/CopyFeedbackButton';
import { LegalDocumentModal } from '../common/LegalDocumentModal';
import {
@@ -2476,12 +2477,12 @@ function RechargePaymentResultModal({
: 'error';
return (
<PlatformStatusDialog
<PlatformAcknowledgeStatusDialog
status={status}
title={result.title}
description={result.message}
onClose={onClose}
action={{ label: '知道了', onClick: onClose }}
actionSurface="profile"
zIndexClassName="z-[90]"
overlayClassName={PROFILE_MODAL_OVERLAY_CLASS}
/>