This commit is contained in:
2026-04-23 03:51:14 +08:00
parent 1223f597d2
commit 8530cd0d8e
11 changed files with 241 additions and 54 deletions

View File

@@ -1,4 +1,6 @@
import type { ReactNode } from 'react';
import { X } from 'lucide-react';
import { type ReactNode, useState } from 'react';
import { createPortal } from 'react-dom';
import type { CustomWorldProfile } from '../../types';
@@ -29,6 +31,81 @@ function SmallButton({
);
}
function PublishBlockersDialog({
blockers,
onClose,
}: {
blockers: string[];
onClose: () => void;
}) {
if (typeof document === 'undefined') {
return null;
}
return createPortal(
<div
className="platform-overlay fixed inset-0 z-[140] flex items-end justify-center bg-slate-950/56 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(88vh,42rem)] w-full max-w-lg 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-white/10 px-5 py-4">
<div>
<div className="text-base font-semibold text-white">
</div>
<div className="mt-1 text-sm leading-6 text-[var(--platform-text-soft)]">
{blockers.length}
</div>
</div>
<button
type="button"
onClick={onClose}
aria-label="关闭"
className="platform-icon-button"
>
<X className="h-4 w-4" />
</button>
</div>
<div className="min-h-0 flex-1 overflow-y-auto px-5 py-4">
<div className="space-y-2">
{blockers.map((blocker, index) => (
<div
key={`publish-blocker-${index}-${blocker}`}
className="rounded-[1.1rem] border border-amber-300/18 bg-amber-500/10 px-4 py-3 text-sm leading-6 text-[var(--platform-text-strong)]"
>
<div className="text-xs font-semibold tracking-[0.14em] text-amber-100/78">
{index + 1}
</div>
<div className="mt-1">{blocker}</div>
</div>
))}
</div>
</div>
<div className="flex justify-end border-t border-white/10 px-5 py-4">
<button
type="button"
onClick={onClose}
className="platform-button platform-button--primary"
>
</button>
</div>
</div>
</div>,
document.body,
);
}
interface RpgCreationResultActionBarProps {
editActionLabel: string;
enterWorldActionLabel: string;
@@ -40,6 +117,7 @@ interface RpgCreationResultActionBarProps {
profile: CustomWorldProfile;
regenerateActionLabel: string;
publishReady: boolean;
publishBlockers: string[];
}
export function RpgCreationResultActionBar({
@@ -53,7 +131,21 @@ export function RpgCreationResultActionBar({
profile,
regenerateActionLabel,
publishReady,
publishBlockers,
}: RpgCreationResultActionBarProps) {
const [showPublishBlockersDialog, setShowPublishBlockersDialog] =
useState(false);
// 结果页保持清爽,只有用户发起发布动作时才弹出阻断项提示。
const handleEnterWorld = () => {
if (!publishReady) {
setShowPublishBlockersDialog(true);
return;
}
onEnterWorld?.();
};
return (
<div className="mt-4 flex flex-col gap-3">
{profile.generationStatus === 'key_only' ? (
@@ -82,14 +174,24 @@ export function RpgCreationResultActionBar({
{onEnterWorld ? (
<button
type="button"
onClick={onEnterWorld}
disabled={isGenerating || !publishReady}
onClick={handleEnterWorld}
disabled={isGenerating}
className={`platform-button platform-button--primary ${isGenerating ? 'opacity-55' : ''}`}
>
{enterWorldActionLabel}
</button>
) : null}
</div>
{showPublishBlockersDialog ? (
<PublishBlockersDialog
blockers={
publishBlockers.length > 0
? publishBlockers
: ['当前草稿还没有通过发布门槛,请先补齐必要内容。']
}
onClose={() => setShowPublishBlockersDialog(false)}
/>
) : null}
</div>
);
}