继续收口图片面板工具弹窗
将 CreativeImageInputPanel 的预览与删除确认弹窗并回 UnifiedModal 体系 补充图片面板预览与遮罩关闭回归测试 更新 PlatformUiKit 收口计划与共享决策记录
This commit is contained in:
@@ -63,6 +63,7 @@
|
||||
- 2026-06-11 追加:`PlatformDarkModalFooter` 继续从标准双按钮 footer 扩到 detail / confirm 收尾;`NpcModals.tsx` 的交易详情 footer 和 `MapModal.tsx` 的场景切换确认 footer 已改成复用同一个 dark footer frame,即使只有单个“关闭”按钮也不再手写 `flex justify-end`。这条抽象继续只覆盖 dark / pixel modal 里的底部分隔线与常规动作区排布,不向白底 profile 弹窗 footer、sticky 工作台 footer 或运行态 HUD 工具条扩张。
|
||||
- 2026-06-11 追加:`PlatformFilterToolbar.tsx` 作为薄结构组件收口 RPG 首页分类工具条;组件只承接“筛选按钮 + tabs + 排序按钮”的排布与 `mobile / desktop` 两种布局差异,不持有筛选状态、空态或排序逻辑。后续只有在同构壳层真的复现时才继续往 `common` 扩覆盖面;如果只是单页内局部重复、接口会越抽越胖,就优先退回文件内 helper。
|
||||
- 2026-06-11 追加:`SquareImageCropModal.tsx` 的白底弹窗壳层改为复用 `UnifiedModal.tsx`,同时给 `UnifiedModal` 薄补 `titleId` 与 `closeIcon` 透传,让裁剪弹窗继续保留自定义 close icon、无 backdrop / Escape 关闭和两列 footer,而不把 `PlatformProfileModalShell` 这类带页面语义的壳层倒灌回 `common/`。这条规则适用于 `common` 级工具弹窗:先看 `UnifiedModal` 能不能承接,再决定是否需要新的薄壳。
|
||||
- 2026-06-11 追加:`CreativeImageInputPanel.tsx` 里参考图预览、主图预览和移除图片确认都继续并回 `UnifiedModal` 体系:两个预览弹窗直接复用 `UnifiedModal`,删除确认直接复用 `UnifiedConfirmDialog`,不再在图片面板里手写三段 `platform-modal-backdrop + platform-modal-shell`。当前没有新增 `PlatformImagePreviewModal`,因为这批差异还只在尺寸与文案层,继续组合已有 modal 原语的 leverage 更高。
|
||||
- 2026-06-11 追加:`PlatformProfileModalShell` 继续补齐标准 footer 插槽,直接透传 `UnifiedModal.footer` 与 `footerClassName`;`RpgEntryHomeView.tsx` 的昵称修改弹窗已改成标准 profile footer,不再把双按钮动作区手写在 body 末尾。后续个人中心里同类“表单内容 + 底部双按钮”弹窗优先走壳层 footer 接法。
|
||||
- 2026-06-11 追加:`PlatformProfileModalShell` 的标准 footer 接法继续扩展到单 CTA 表单收尾;`PlatformProfileRewardCodeRedeemModal.tsx` 的兑换按钮已迁到壳层 footer,body 只保留输入和反馈消息。`PlatformAsyncStatePanel` 同日继续扩展到 `PlatformAssetPickerGrid`、`VisualNovelSavePanel.tsx` 与 `AccountModal.tsx` 的账号安全三个子区块;其中公共素材网格继续把 `error` banner 放在状态壳外层,保持错误提示可与加载态或内容并存的原语义。
|
||||
- 2026-06-11 追加:按钮层继续补齐轻量漏网项。`PlatformTagEditor.tsx` 的标签 chip 删除入口已改成紧凑 `PlatformIconButton`,保留透明背景和原 chip 高度;`RpgEntryCharacterSelectView.tsx` 的两处“返回”按钮统一沉到局部 `CharacterSelectBackButton`,底层委托 `PlatformActionButton surface="editorDark"`。同日 `GenerationProgressHero.tsx` 新增 `GenerationHeaderBackButton`,`CustomWorldGenerationView.tsx` 与 `BarkBattleGeneratingView.tsx` 已开始复用这套暖色生成页返回入口骨架;后续同类轻量返回按钮与 chip 删除按钮优先继续沿共享按钮 + 薄包装的方向推进。
|
||||
|
||||
@@ -273,6 +273,7 @@
|
||||
19.3.47. `PlatformDarkModalFooter` 继续从标准双按钮 footer 扩展到 detail / confirm 收尾:`NpcModals.tsx` 的交易详情单按钮 footer 与 `MapModal.tsx` 的场景切换确认 footer 已接入共享 dark footer frame,分别保留“关闭”单 CTA 和“取消 / 确认前往”双 CTA 的业务语义、按钮 tone 与禁用态。后续 dark / pixel modal 里若只是标准底部分隔线 + 常规动作区排布,优先直接复用 `PlatformDarkModalFooter`,即使只有单个按钮也不再手写 `flex justify-end`;但像 `SquareImageCropModal.tsx` 这类白底弹窗 footer、sticky 工作台 footer 和运行态 HUD 工具条继续留在各自语义壳层,不强行混到 dark footer 抽象里。验证命令:`npx vitest run src/components/NpcModals.test.tsx src/components/MapModal.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
|
||||
19.3.48. `RpgEntryHomeView.tsx` 里的分类筛选工具条继续从页面内重复 JSX 收口到 `src/components/common/PlatformFilterToolbar.tsx`;该 Module 只承接“筛选按钮 + 横向 tabs + 排序按钮”的结构排布,暴露 `mobile / desktop` 两种 layout 以覆盖移动端 divider + 独立排序行和桌面端同排布局差异,但不持有分类列表、筛选状态、空态或排序逻辑。当前 RPG 首页分类区已接入,后续若其它白底列表页也出现同构的筛选壳层,可直接复用这套薄结构组件;若场景只是在单页内局部重复、接口会为了兼容业务差异不断膨胀,则优先退回文件内 helper,不把 `common` 扩成假的“万能筛选条”。验证命令:`npx vitest run src/components/common/PlatformFilterToolbar.test.tsx src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
|
||||
19.3.49. `SquareImageCropModal.tsx` 的白底 modal 壳层与 footer 已收口到 `src/components/common/UnifiedModal.tsx`;`UnifiedModal` 为此只薄补了 `titleId` 与 `closeIcon` 透传,继续由调用方决定 `closeOnBackdrop`、`closeOnEscape`、`portal`、header/footer 样式和按钮内容,不额外掺入 profile 业务语义,也不让 `common/` 反向依赖 `platform-entry/`。`SquareImageCropModal.tsx` 继续保留裁剪拖拽、pointer capture、保存禁用态与两列等宽 footer 行为,只把 header / body / footer 外壳交给共享 modal 承接。后续 `common` 级白底工具弹窗若只是标准标题栏 + 内容区 + footer 按钮排布,优先先看 `UnifiedModal` 是否够用,再决定是否需要新的薄壳;不要为了一个弹窗把 `PlatformProfileModalShell` 之类带页面语义的壳层倒灌回 `common`。验证命令:`npx vitest run src/components/common/SquareImageCropModal.test.tsx src/components/common/UnifiedModal.test.tsx src/components/unified-creation/workspaces/PuzzleCreationWorkspace.interaction.test.tsx src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
|
||||
19.3.50. `CreativeImageInputPanel.tsx` 里内嵌的 white tool modal 继续并回 `UnifiedModal` 体系:参考图预览与主图预览都改成直接复用 `src/components/common/UnifiedModal.tsx`,继续保留各自 `max-w` / `max-h` 节奏、点击遮罩关闭与紧凑 header;移除图片确认改成复用 `src/components/common/UnifiedConfirmDialog.tsx`,不再在 panel 内手写 `platform-modal-backdrop + platform-modal-shell + 两列按钮`。这次没有新增 `PlatformImagePreviewModal`,因为当前预览弹窗差异还只在尺寸和文案层,继续直接组合 `UnifiedModal` 更深、更稳。后续 `common` 级图片面板若出现同类“预览大图 + 单标题栏 + 关闭按钮”弹窗,优先先复用 `UnifiedModal` 并把尺寸/文案留在调用方;只有当至少两到三个调用点开始重复同一套 preview body/header adapter 时,再考虑补新的薄壳。验证命令:`npx vitest run src/components/common/CreativeImageInputPanel.test.tsx src/components/common/UnifiedModal.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
|
||||
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`。
|
||||
|
||||
@@ -106,6 +106,7 @@ test('creative image input panel handles reference uploads and preview', () => {
|
||||
expect.stringContaining('ref-1'),
|
||||
);
|
||||
fireEvent.click(screen.getByRole('button', { name: '关闭参考图预览' }));
|
||||
expect(screen.queryByRole('dialog', { name: '参考图 1' })).toBeNull();
|
||||
fireEvent.click(screen.getByRole('button', { name: '移除参考图 参考图 1' }));
|
||||
expect(onPromptReferenceRemove).toHaveBeenCalledWith('ref-1');
|
||||
|
||||
@@ -255,6 +256,54 @@ test('creative image input panel confirms before removing uploaded image', () =>
|
||||
expect(onMainImageRemove).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('creative image input panel closes reference preview on backdrop click', () => {
|
||||
render(
|
||||
<CreativeImageInputPanel
|
||||
uploadedImageSrc=""
|
||||
uploadedImageAlt="拼图图片"
|
||||
mainImageInputId="image-upload-input"
|
||||
promptTextareaId="image-prompt-input"
|
||||
prompt="旧街灯牌下的猫。"
|
||||
promptLabel="画面描述"
|
||||
aiRedraw
|
||||
promptReferenceImages={[
|
||||
{
|
||||
id: 'ref-1',
|
||||
label: '参考图 1',
|
||||
imageSrc: 'data:image/png;base64,ref-1',
|
||||
},
|
||||
]}
|
||||
imageModelPicker={null}
|
||||
submitLabel="生成"
|
||||
submitDisabled={false}
|
||||
labels={{
|
||||
imageField: '拼图画面',
|
||||
uploadImage: '上传拼图图片',
|
||||
replaceImage: '更换拼图图片',
|
||||
emptyImageHint: '上传图片/填写画面描述',
|
||||
removeImage: '移除拼图图片',
|
||||
removeImageConfirmTitle: '移除拼图图片?',
|
||||
removeImageConfirmBody: '移除后需要重新上传图片。',
|
||||
promptReferenceUpload: '上传参考图',
|
||||
promptReferencePreviewAlt: '参考图预览',
|
||||
closePromptReferencePreview: '关闭参考图预览',
|
||||
}}
|
||||
onMainImageFileSelect={() => {}}
|
||||
onMainImageRemove={() => {}}
|
||||
onAiRedrawChange={() => {}}
|
||||
onPromptChange={() => {}}
|
||||
onPromptReferenceFilesSelect={() => {}}
|
||||
onPromptReferenceRemove={() => {}}
|
||||
onSubmit={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '预览参考图 参考图 1' }));
|
||||
const dialog = screen.getByRole('dialog', { name: '参考图 1' });
|
||||
fireEvent.click(dialog.parentElement as HTMLElement);
|
||||
expect(screen.queryByRole('dialog', { name: '参考图 1' })).toBeNull();
|
||||
});
|
||||
|
||||
test('creative image input panel supports a preview-only main image mode', () => {
|
||||
const onSubmit = vi.fn();
|
||||
|
||||
@@ -356,6 +405,9 @@ test('creative image input panel can preview the main image and keep upload on a
|
||||
2,
|
||||
);
|
||||
fireEvent.click(screen.getByRole('button', { name: '关闭关卡图片预览' }));
|
||||
expect(
|
||||
screen.queryByRole('dialog', { name: '查看关卡图片' }),
|
||||
).toBeNull();
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '更换参考图' }));
|
||||
expect(inputClickSpy).toHaveBeenCalledTimes(1);
|
||||
@@ -376,6 +428,49 @@ test('creative image input panel can preview the main image and keep upload on a
|
||||
}
|
||||
});
|
||||
|
||||
test('creative image input panel closes main image preview on backdrop click', () => {
|
||||
render(
|
||||
<CreativeImageInputPanel
|
||||
mainImageClickMode="preview"
|
||||
uploadedImageSrc="/generated-puzzle-assets/session/level/image.png"
|
||||
uploadedImageAlt="拼图关卡图"
|
||||
mainImageInputId="level-image-upload-input"
|
||||
promptTextareaId="level-prompt-input"
|
||||
prompt="旧街灯牌下的猫。"
|
||||
promptLabel="画面描述"
|
||||
aiRedraw
|
||||
promptReferenceImages={[]}
|
||||
imageModelPicker={null}
|
||||
submitLabel="重新生成画面"
|
||||
submitDisabled={false}
|
||||
labels={{
|
||||
imageField: '画面图',
|
||||
uploadImage: '上传参考图',
|
||||
replaceImage: '更换参考图',
|
||||
emptyImageHint: '上传图片/填写画面描述',
|
||||
removeImage: '移除参考图',
|
||||
removeImageConfirmTitle: '移除参考图?',
|
||||
removeImageConfirmBody: '移除后可重新上传或选择历史图片。',
|
||||
promptReferenceUpload: '上传参考图',
|
||||
promptReferencePreviewAlt: '参考图预览',
|
||||
closePromptReferencePreview: '关闭参考图预览',
|
||||
previewMainImage: '查看关卡图片',
|
||||
closeMainImagePreview: '关闭关卡图片预览',
|
||||
}}
|
||||
onMainImageFileSelect={() => {}}
|
||||
onMainImageRemove={() => {}}
|
||||
onAiRedrawChange={() => {}}
|
||||
onPromptChange={() => {}}
|
||||
onSubmit={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '查看关卡图片' }));
|
||||
const dialog = screen.getByRole('dialog', { name: '查看关卡图片' });
|
||||
fireEvent.click(dialog.parentElement as HTMLElement);
|
||||
expect(screen.queryByRole('dialog', { name: '查看关卡图片' })).toBeNull();
|
||||
});
|
||||
|
||||
test('creative image input panel can hide upload and history controls independently', () => {
|
||||
render(
|
||||
<CreativeImageInputPanel
|
||||
|
||||
@@ -6,12 +6,13 @@ import { PlatformActionButton } from './PlatformActionButton';
|
||||
import { PlatformFieldLabel } from './PlatformFieldLabel';
|
||||
import { PlatformIconBadge } from './PlatformIconBadge';
|
||||
import { PlatformIconButton } from './PlatformIconButton';
|
||||
import { PlatformModalCloseButton } from './PlatformModalCloseButton';
|
||||
import { PlatformPillBadge } from './PlatformPillBadge';
|
||||
import { PlatformPillSwitch } from './PlatformPillSwitch';
|
||||
import { PlatformStatusMessage } from './PlatformStatusMessage';
|
||||
import { PlatformTextField } from './PlatformTextField';
|
||||
import { PlatformUploadPreviewCard } from './PlatformUploadPreviewCard';
|
||||
import { UnifiedConfirmDialog } from './UnifiedConfirmDialog';
|
||||
import { UnifiedModal } from './UnifiedModal';
|
||||
|
||||
export type CreativeImageInputReferenceImage = {
|
||||
id: string;
|
||||
@@ -489,120 +490,76 @@ export function CreativeImageInputPanel({
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{previewReferenceImage ? (
|
||||
<div
|
||||
className="platform-modal-backdrop fixed inset-0 z-[80] flex items-center justify-center px-4 py-6"
|
||||
onClick={() => setPreviewReferenceImage(null)}
|
||||
>
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="creative-image-reference-preview-title"
|
||||
className="platform-modal-shell platform-remap-surface w-full max-w-2xl rounded-[1.35rem] p-3 shadow-[0_24px_70px_rgba(15,23,42,0.22)]"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<div className="mb-3 flex items-center justify-between gap-3 px-1">
|
||||
<div
|
||||
id="creative-image-reference-preview-title"
|
||||
className="min-w-0 truncate text-sm font-black text-[var(--platform-text-strong)]"
|
||||
>
|
||||
{previewReferenceImage.label}
|
||||
</div>
|
||||
<PlatformModalCloseButton
|
||||
label={labels.closePromptReferencePreview}
|
||||
variant="profileCompact"
|
||||
onClick={() => setPreviewReferenceImage(null)}
|
||||
className="shrink-0"
|
||||
/>
|
||||
</div>
|
||||
<div className="max-h-[72vh] overflow-hidden rounded-[1rem] bg-black/5">
|
||||
<ResolvedAssetImage
|
||||
src={previewReferenceImage.imageSrc}
|
||||
alt={labels.promptReferencePreviewAlt}
|
||||
className="h-full max-h-[72vh] w-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
<UnifiedModal
|
||||
open={Boolean(previewReferenceImage)}
|
||||
title={previewReferenceImage?.label ?? labels.promptReferencePreviewAlt}
|
||||
onClose={() => setPreviewReferenceImage(null)}
|
||||
closeLabel={labels.closePromptReferencePreview}
|
||||
closeVariant="profileCompact"
|
||||
size="lg"
|
||||
zIndexClassName="z-[80]"
|
||||
overlayClassName="px-4 py-6"
|
||||
panelClassName="platform-remap-surface rounded-[1.35rem] p-3 shadow-[0_24px_70px_rgba(15,23,42,0.22)]"
|
||||
headerClassName="mb-3 items-center border-b-0 px-1 py-0"
|
||||
titleClassName="text-sm font-black"
|
||||
bodyClassName="px-0 py-0"
|
||||
>
|
||||
{previewReferenceImage ? (
|
||||
<div className="max-h-[72vh] overflow-hidden rounded-[1rem] bg-black/5">
|
||||
<ResolvedAssetImage
|
||||
src={previewReferenceImage.imageSrc}
|
||||
alt={labels.promptReferencePreviewAlt}
|
||||
className="h-full max-h-[72vh] w-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
) : null}
|
||||
</UnifiedModal>
|
||||
|
||||
{isMainImagePreviewOpen && uploadedImageSrc ? (
|
||||
<div
|
||||
className="platform-modal-backdrop fixed inset-0 z-[82] flex items-center justify-center px-4 py-6"
|
||||
onClick={() => setIsMainImagePreviewOpen(false)}
|
||||
>
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="creative-image-main-preview-title"
|
||||
className="platform-modal-shell platform-remap-surface w-full max-w-4xl rounded-[1.35rem] p-3 shadow-[0_24px_70px_rgba(15,23,42,0.22)]"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<div className="mb-3 flex items-center justify-between gap-3 px-1">
|
||||
<div
|
||||
id="creative-image-main-preview-title"
|
||||
className="min-w-0 truncate text-sm font-black text-[var(--platform-text-strong)]"
|
||||
>
|
||||
{labels.previewMainImage ?? uploadedImageAlt}
|
||||
</div>
|
||||
<PlatformModalCloseButton
|
||||
label={
|
||||
labels.closeMainImagePreview ??
|
||||
labels.closePromptReferencePreview
|
||||
}
|
||||
variant="profileCompact"
|
||||
onClick={() => setIsMainImagePreviewOpen(false)}
|
||||
className="shrink-0"
|
||||
/>
|
||||
</div>
|
||||
<div className="max-h-[82vh] overflow-hidden rounded-[1rem] bg-black/5">
|
||||
<ResolvedAssetImage
|
||||
src={uploadedImageSrc}
|
||||
refreshKey={uploadedImageRefreshKey}
|
||||
alt={uploadedImageAlt}
|
||||
className="h-full max-h-[82vh] w-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
<UnifiedModal
|
||||
open={isMainImagePreviewOpen && Boolean(uploadedImageSrc)}
|
||||
title={labels.previewMainImage ?? uploadedImageAlt}
|
||||
onClose={() => setIsMainImagePreviewOpen(false)}
|
||||
closeLabel={
|
||||
labels.closeMainImagePreview ?? labels.closePromptReferencePreview
|
||||
}
|
||||
closeVariant="profileCompact"
|
||||
size="xl"
|
||||
zIndexClassName="z-[82]"
|
||||
overlayClassName="px-4 py-6"
|
||||
panelClassName="platform-remap-surface rounded-[1.35rem] p-3 shadow-[0_24px_70px_rgba(15,23,42,0.22)]"
|
||||
headerClassName="mb-3 items-center border-b-0 px-1 py-0"
|
||||
titleClassName="text-sm font-black"
|
||||
bodyClassName="px-0 py-0"
|
||||
>
|
||||
{uploadedImageSrc ? (
|
||||
<div className="max-h-[82vh] overflow-hidden rounded-[1rem] bg-black/5">
|
||||
<ResolvedAssetImage
|
||||
src={uploadedImageSrc}
|
||||
refreshKey={uploadedImageRefreshKey}
|
||||
alt={uploadedImageAlt}
|
||||
className="h-full max-h-[82vh] w-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
) : null}
|
||||
</UnifiedModal>
|
||||
|
||||
{isRemoveImageConfirmOpen ? (
|
||||
<div className="platform-modal-backdrop fixed inset-0 z-[80] flex items-center justify-center px-4 py-6">
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="creative-image-remove-confirm-title"
|
||||
className="platform-modal-shell platform-remap-surface w-full max-w-xs rounded-[1.35rem] p-5 shadow-[0_24px_70px_rgba(15,23,42,0.22)]"
|
||||
>
|
||||
<div
|
||||
id="creative-image-remove-confirm-title"
|
||||
className="text-base font-black text-[var(--platform-text-strong)]"
|
||||
>
|
||||
{labels.removeImageConfirmTitle}
|
||||
</div>
|
||||
<div className="mt-2 text-sm leading-6 text-[var(--platform-text-base)]">
|
||||
{labels.removeImageConfirmBody}
|
||||
</div>
|
||||
<div className="mt-5 grid grid-cols-2 gap-3">
|
||||
<PlatformActionButton
|
||||
tone="secondary"
|
||||
onClick={() => setIsRemoveImageConfirmOpen(false)}
|
||||
>
|
||||
取消
|
||||
</PlatformActionButton>
|
||||
<PlatformActionButton
|
||||
onClick={() => {
|
||||
onMainImageRemove();
|
||||
setIsRemoveImageConfirmOpen(false);
|
||||
}}
|
||||
>
|
||||
移除
|
||||
</PlatformActionButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<UnifiedConfirmDialog
|
||||
open={isRemoveImageConfirmOpen}
|
||||
title={labels.removeImageConfirmTitle}
|
||||
description={labels.removeImageConfirmBody}
|
||||
onClose={() => setIsRemoveImageConfirmOpen(false)}
|
||||
confirmLabel="移除"
|
||||
cancelLabel="取消"
|
||||
showCancel
|
||||
onConfirm={() => {
|
||||
onMainImageRemove();
|
||||
setIsRemoveImageConfirmOpen(false);
|
||||
}}
|
||||
size="sm"
|
||||
zIndexClassName="z-[80]"
|
||||
overlayClassName="px-4 py-6"
|
||||
panelClassName="platform-remap-surface max-w-xs rounded-[1.35rem] shadow-[0_24px_70px_rgba(15,23,42,0.22)]"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user