修复泥点弹窗透明问题

为泥点消耗确认弹窗补齐平台主题作用域和模态面板样式

让平台状态弹窗合并默认主题遮罩,避免自定义遮罩覆盖主题变量

补充弹窗默认样式测试和团队排障记录
This commit is contained in:
2026-06-12 15:24:28 +08:00
parent cfc0c0eadf
commit 93e4522b65
5 changed files with 83 additions and 5 deletions

View File

@@ -200,6 +200,14 @@
- 验证:`npm test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "puzzle form checks mud points before creating a draft|match3d form checks mud points before creating a draft|bark battle form checks mud points before creating image assets"` 应断言弹窗出现、对应工作台仍在、玩法模板分类不再出现。
- 关联:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx``src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx``docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`
## 内嵌泥点确认弹窗必须自带平台主题作用域
- 现象:拼图 / 抓大鹅统一创作页点击生成后,“确认消耗泥点”弹窗正文和按钮存在,但弹窗面板背景透明,只剩遮罩和文字。
- 原因:`PlatformMudPointConfirmDialog` 作为二级确认常以 `portal={false}` 内嵌到工作台局部 DOM局部节点不一定继承 `.platform-theme``platform-modal-shell` 依赖 `--platform-modal-fill` 等主题变量,变量缺失时面板底色解析为空。
- 处理:共享泥点确认弹窗默认在 overlay 上带 `platform-theme platform-theme--<theme>``platform-modal-backdrop` 和实色遮罩,在 panel 上带 `platform-modal-shell platform-remap-surface`;单按钮状态弹窗也要有默认 light 主题,避免未来独立调用复现。
- 验证:浏览器触发 `/creation/puzzle``/creation/match3d` 的泥点确认弹窗,检查 overlay 最近主题 class 存在、`--platform-modal-fill` 有值且面板为实底;聚焦测试覆盖默认 overlay / panel class。
- 关联:`src/components/common/PlatformMudPointConfirmDialog.tsx``src/components/common/PlatformStatusDialog.tsx``src/components/unified-creation/workspaces/PuzzleCreationWorkspace.tsx``src/components/unified-creation/workspaces/Match3DCreationWorkspace.tsx`
## 玩法入口分类字段缺失要前端兜底
- 现象:平台创作入口初始化时,`platformEntryCreationTypes.ts` 直接对 `creationTypes[].categoryId` / `categoryLabel``trim()`,一旦后端旧数据、局部 mock 或异常返回里缺字段,整个创作页会在 `derivePlatformCreationTypes(...)` 里直接炸掉。

View File

@@ -51,3 +51,27 @@ test('supports extra detail copy and close button override', () => {
expect(within(dialog).getByText('本次会覆盖当前待确认素材。')).toBeTruthy();
expect(screen.queryByRole('button', { name: '关闭' })).toBeNull();
});
test('applies the stronger default overlay and panel chrome', () => {
render(
<PlatformMudPointConfirmDialog
open
points={10}
onClose={() => {}}
onConfirm={() => {}}
portal={false}
/>,
);
const dialog = screen.getByRole('dialog', { name: '确认消耗泥点' });
const overlay = dialog.parentElement as HTMLElement;
expect(overlay.className).toContain('platform-modal-backdrop');
expect(overlay.className).toContain('platform-theme--light');
expect(overlay.className).toContain('!bg-black/45');
expect(dialog.className).toContain('platform-modal-shell');
expect(dialog.className).toContain('max-w-xs');
expect(dialog.className).toContain(
'shadow-[0_24px_70px_rgba(15,23,42,0.22)]',
);
});

View File

@@ -1,5 +1,6 @@
import type { ReactNode } from 'react';
import { useAuthUi } from '../auth/AuthUiContext';
import { UnifiedConfirmDialog } from './UnifiedConfirmDialog';
type PlatformMudPointConfirmDialogProps = {
@@ -19,6 +20,14 @@ type PlatformMudPointConfirmDialogProps = {
panelClassName?: string;
};
const DEFAULT_OVERLAY_CLASS_NAME = 'platform-modal-backdrop z-[80] !bg-black/45';
const DEFAULT_PANEL_CLASS_NAME =
'platform-modal-shell platform-remap-surface max-w-xs rounded-[1.35rem] shadow-[0_24px_70px_rgba(15,23,42,0.22)]';
function joinClassNames(...classNames: Array<string | undefined>) {
return classNames.filter(Boolean).join(' ');
}
/**
* 平台泥点消耗确认弹窗。
* 统一承接“确认消耗泥点 + 消耗 N 泥点”的标准确认壳层,业务侧只保留点数与确认动作。
@@ -39,6 +48,8 @@ export function PlatformMudPointConfirmDialog({
overlayClassName,
panelClassName,
}: PlatformMudPointConfirmDialogProps) {
const resolvedPlatformTheme = useAuthUi()?.platformTheme ?? 'light';
return (
<UnifiedConfirmDialog
open={open}
@@ -52,8 +63,12 @@ export function PlatformMudPointConfirmDialog({
showCloseButton={showCloseButton}
portal={portal}
size={size}
overlayClassName={overlayClassName}
panelClassName={panelClassName}
overlayClassName={joinClassNames(
`platform-theme platform-theme--${resolvedPlatformTheme}`,
DEFAULT_OVERLAY_CLASS_NAME,
overlayClassName,
)}
panelClassName={joinClassNames(DEFAULT_PANEL_CLASS_NAME, panelClassName)}
>
<div className="space-y-2">
<div className="font-semibold"> {points} </div>

View File

@@ -21,6 +21,7 @@ test('renders result state with description and primary action', () => {
);
const dialog = screen.getByRole('dialog', { name: '支付成功' });
const overlay = dialog.parentElement as HTMLElement;
const badge = dialog.querySelector('.platform-icon-badge');
const action = screen.getByRole('button', { name: '知道了' });
const visibleDescription = dialog.querySelector(
@@ -28,6 +29,8 @@ test('renders result state with description and primary action', () => {
);
expect(dialog).toBeTruthy();
expect(overlay.className).toContain('platform-theme--light');
expect(overlay.className).toContain('platform-profile-modal-overlay');
expect(visibleDescription?.textContent).toBe('账户状态已刷新');
expect(badge?.className).toContain('text-[var(--platform-success-text)]');
expect(action.className).toContain('platform-primary-button');
@@ -97,6 +100,27 @@ test('supports custom badge icon label and action button styling', () => {
expect(action.className).toContain('bg-slate-950');
});
test('keeps default theme classes when callers customize the overlay', () => {
render(
<PlatformStatusDialog
status="error"
title="发布失败"
description="图片生成失败"
onClose={vi.fn()}
overlayClassName="bg-slate-950/58 custom-overlay"
action={{ label: '知道了', onClick: vi.fn(), surface: 'platform' }}
/>,
);
const dialog = screen.getByRole('dialog', { name: '发布失败' });
const overlay = dialog.parentElement as HTMLElement;
expect(overlay.className).toContain('platform-theme--light');
expect(overlay.className).toContain('platform-profile-modal-overlay');
expect(overlay.className).toContain('bg-slate-950/58');
expect(overlay.className).toContain('custom-overlay');
});
test('supports header notice layout with body content and close button', () => {
const onClose = vi.fn();

View File

@@ -64,11 +64,15 @@ type PlatformStatusVisualConfig = {
};
const DEFAULT_OVERLAY_CLASS =
'platform-profile-modal-overlay bg-slate-950/72 backdrop-blur-xl';
'platform-theme platform-theme--light platform-profile-modal-overlay bg-slate-950/72 backdrop-blur-xl';
const DEFAULT_PANEL_CLASS =
'platform-remap-surface !max-w-sm rounded-[1.4rem]';
const DEFAULT_BODY_CLASS = 'px-5 pb-5 pt-6 text-center';
function joinClassNames(...classNames: Array<string | undefined>) {
return classNames.filter(Boolean).join(' ');
}
function getStatusVisualConfig(
status: PlatformStatusDialogStatus,
): PlatformStatusVisualConfig {
@@ -119,7 +123,7 @@ export function PlatformStatusDialog({
closeLabel,
closeDisabled = false,
zIndexClassName = 'z-[90]',
overlayClassName = DEFAULT_OVERLAY_CLASS,
overlayClassName,
panelClassName = DEFAULT_PANEL_CLASS,
bodyClassName = DEFAULT_BODY_CLASS,
iconClassName,
@@ -144,7 +148,10 @@ export function PlatformStatusDialog({
portal={false}
size="sm"
zIndexClassName={zIndexClassName}
overlayClassName={overlayClassName}
overlayClassName={joinClassNames(
DEFAULT_OVERLAY_CLASS,
overlayClassName,
)}
panelClassName={panelClassName}
bodyClassName={bodyClassName}
>