feat: add workflow page feature toggles
This commit is contained in:
parent
9422c5de73
commit
076e4f55e6
@ -33,6 +33,7 @@ import {
|
||||
import {
|
||||
AGENT_WORKFLOW_PROMPT_FRAGMENT_MAX_LENGTH,
|
||||
defaultAgentWorkflowConstraintSettings,
|
||||
defaultAgentWorkflowPageFeatureSettings,
|
||||
AGENT_WORKFLOW_MAX_OPTION_DEPTH,
|
||||
AGENT_WORKFLOW_MAX_TREE_DEPTH,
|
||||
countAgentWorkflowOptions,
|
||||
@ -46,6 +47,7 @@ import {
|
||||
getEnabledAgentWorkflowOptionTree,
|
||||
getAgentWorkflowCategoryLabels,
|
||||
getAgentWorkflowConstraintSettings,
|
||||
getAgentWorkflowPageFeatureSettings,
|
||||
normalizeAgentWorkflowOptionOrdering,
|
||||
flattenAgentWorkflowSteps,
|
||||
normalizeAgentWorkflowStepOrdering,
|
||||
@ -1511,6 +1513,10 @@ export function AgentWorkflowSettingsClient({
|
||||
() => getAgentWorkflowConstraintSettings(config),
|
||||
[config]
|
||||
);
|
||||
const workflowPageFeatureSettings = useMemo(
|
||||
() => getAgentWorkflowPageFeatureSettings(config),
|
||||
[config]
|
||||
);
|
||||
const firstCategoryLabel = categoryLabels.firstCategory;
|
||||
const secondCategoryLabel = categoryLabels.secondCategory;
|
||||
const thirdCategoryLabel = categoryLabels.thirdCategory;
|
||||
@ -4642,6 +4648,91 @@ export function AgentWorkflowSettingsClient({
|
||||
variants={containerVariants}
|
||||
className="grid gap-6 lg:grid-cols-2"
|
||||
>
|
||||
<motion.div variants={itemVariants} className="lg:col-span-2">
|
||||
<Card>
|
||||
<CardHeader className="pb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-violet-500/10">
|
||||
<Settings2 className="h-4 w-4 text-violet-600" />
|
||||
</div>
|
||||
<div>
|
||||
<CardTitle className="text-base">前台流程功能开关</CardTitle>
|
||||
<CardDescription>
|
||||
控制风格化页面是否展示选择生图模式、选择 SPU / SKU 和套 PSD 模板入口
|
||||
</CardDescription>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="grid gap-3 md:grid-cols-3">
|
||||
<div className="flex items-center justify-between rounded-lg border bg-muted/20 px-4 py-3">
|
||||
<div className="pr-4">
|
||||
<div className="text-sm font-medium">开启第一步:选择生图模式</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
关闭后前台不再要求选择生图模式,直接进入后续配置并选择模型生图。
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
checked={workflowPageFeatureSettings.presetStepEnabled}
|
||||
onCheckedChange={(checked) =>
|
||||
setConfig((prev) => ({
|
||||
...prev,
|
||||
pageFeatureSettings: {
|
||||
...(prev.pageFeatureSettings ||
|
||||
defaultAgentWorkflowPageFeatureSettings),
|
||||
presetStepEnabled: checked,
|
||||
},
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between rounded-lg border bg-muted/20 px-4 py-3">
|
||||
<div className="pr-4">
|
||||
<div className="text-sm font-medium">开启第二步:选择 SPU / SKU</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
关闭后前台跳过商品素材搜索,不再强制补齐 SPU / SKU 输入图槽位。
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
checked={workflowPageFeatureSettings.materialStepEnabled}
|
||||
onCheckedChange={(checked) =>
|
||||
setConfig((prev) => ({
|
||||
...prev,
|
||||
pageFeatureSettings: {
|
||||
...(prev.pageFeatureSettings ||
|
||||
defaultAgentWorkflowPageFeatureSettings),
|
||||
materialStepEnabled: checked,
|
||||
},
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between rounded-lg border bg-muted/20 px-4 py-3">
|
||||
<div className="pr-4">
|
||||
<div className="text-sm font-medium">开启套 PSD 模板</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
关闭后前台隐藏所有“套PSD”按钮和 PSD 模板编辑弹窗入口。
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
checked={workflowPageFeatureSettings.psdTemplateEnabled}
|
||||
onCheckedChange={(checked) =>
|
||||
setConfig((prev) => ({
|
||||
...prev,
|
||||
pageFeatureSettings: {
|
||||
...(prev.pageFeatureSettings ||
|
||||
defaultAgentWorkflowPageFeatureSettings),
|
||||
psdTemplateEnabled: checked,
|
||||
},
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
|
||||
<motion.div variants={itemVariants} className="lg:col-span-2">
|
||||
<Card>
|
||||
<CardHeader className="pb-4">
|
||||
|
||||
@ -1060,6 +1060,13 @@ const updateSchema = z.object({
|
||||
crossLevelMutualExclusionEnabled: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
pageFeatureSettings: z
|
||||
.object({
|
||||
presetStepEnabled: z.boolean().optional(),
|
||||
materialStepEnabled: z.boolean().optional(),
|
||||
psdTemplateEnabled: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
steps: z
|
||||
.array(agentWorkflowStepSchema)
|
||||
.max(20),
|
||||
|
||||
@ -177,6 +177,7 @@ import {
|
||||
defaultAgentWorkflowUiConfig,
|
||||
flattenVisibleAgentWorkflowSteps,
|
||||
getEnabledAgentWorkflowStepTree,
|
||||
getAgentWorkflowPageFeatureSettings,
|
||||
normalizeAgentWorkflowUiConfig,
|
||||
sanitizeAgentWorkflowSelections,
|
||||
} from "@/lib/agents/workflow-ui-config";
|
||||
@ -1552,6 +1553,11 @@ export default function CanvasEditorLegacyPage({ params }: PageProps) {
|
||||
agentWorkflowBrandModelConfig?: unknown;
|
||||
agentPsdTemplateCategoryTagIndex?: AgentPsdTemplateCategoryTagIndex;
|
||||
agentWorkflowUiConfig?: {
|
||||
pageFeatureSettings?: {
|
||||
presetStepEnabled?: boolean;
|
||||
materialStepEnabled?: boolean;
|
||||
psdTemplateEnabled?: boolean;
|
||||
};
|
||||
steps?: Array<{
|
||||
key: AgentWorkflowStepKey;
|
||||
title: string;
|
||||
@ -1600,6 +1606,14 @@ export default function CanvasEditorLegacyPage({ params }: PageProps) {
|
||||
normalizeAgentWorkflowUiConfig(siteSettingsData?.agentWorkflowUiConfig),
|
||||
[siteSettingsData?.agentWorkflowUiConfig]
|
||||
);
|
||||
const workflowPageFeatureSettings = useMemo(
|
||||
() => getAgentWorkflowPageFeatureSettings(resolvedWorkflowUiConfig),
|
||||
[resolvedWorkflowUiConfig]
|
||||
);
|
||||
const isWorkflowPresetStepEnabled =
|
||||
workflowPageFeatureSettings.presetStepEnabled !== false;
|
||||
const isWorkflowMaterialStepEnabled =
|
||||
workflowPageFeatureSettings.materialStepEnabled !== false;
|
||||
const sanitizedWorkflowSelections = useMemo(
|
||||
() =>
|
||||
sanitizeAgentWorkflowSelections({
|
||||
@ -8152,11 +8166,14 @@ export default function CanvasEditorLegacyPage({ params }: PageProps) {
|
||||
selections: workflowSelectionsRef.current,
|
||||
});
|
||||
}, [resolvedWorkflowUiConfig]);
|
||||
const effectiveSelectedAgentPresetId = isWorkflowPresetStepEnabled
|
||||
? selectedAgentPresetId
|
||||
: null;
|
||||
const { data: selectedAgentPresetResponse } = useSWR<{
|
||||
success?: boolean;
|
||||
data?: AgentPreset;
|
||||
}>(
|
||||
selectedAgentPresetId ? `/api/agents/${selectedAgentPresetId}` : null,
|
||||
effectiveSelectedAgentPresetId ? `/api/agents/${effectiveSelectedAgentPresetId}` : null,
|
||||
fetcher
|
||||
);
|
||||
const selectedAgentPresetData = useMemo(
|
||||
@ -8519,35 +8536,39 @@ export default function CanvasEditorLegacyPage({ params }: PageProps) {
|
||||
/>
|
||||
}
|
||||
brandStorePanel={
|
||||
<AgentWorkflowBrandStorePanel
|
||||
config={resolvedWorkflowUiConfig}
|
||||
inputs={workflowInputs}
|
||||
onInputsChange={handleWorkflowInputsChange}
|
||||
psdTemplateCategoryTagIndex={
|
||||
siteSettingsData?.agentPsdTemplateCategoryTagIndex
|
||||
}
|
||||
theme="dark"
|
||||
/>
|
||||
isWorkflowPresetStepEnabled ? (
|
||||
<AgentWorkflowBrandStorePanel
|
||||
config={resolvedWorkflowUiConfig}
|
||||
inputs={workflowInputs}
|
||||
onInputsChange={handleWorkflowInputsChange}
|
||||
psdTemplateCategoryTagIndex={
|
||||
siteSettingsData?.agentPsdTemplateCategoryTagIndex
|
||||
}
|
||||
theme="dark"
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
chatId={agentModeChatId}
|
||||
deletedResultCollections={agentProjectDeletedResultCollections}
|
||||
getLatestWorkflowContext={getLatestWorkflowContext}
|
||||
materialSelectionPanel={
|
||||
<AgentWorkflowMaterialSelectionPanel
|
||||
appearanceRecognitionEnabled={appearanceRecognitionEnabled}
|
||||
disabled={!selectedAgentPresetId}
|
||||
inputs={workflowInputs}
|
||||
materials={workflowMaterials}
|
||||
modelImageRequirement={materialModelImageRequirement}
|
||||
onAppearanceRecognitionEnabledChange={
|
||||
handleAppearanceRecognitionEnabledChange
|
||||
}
|
||||
onInputsChange={handleWorkflowInputsChange}
|
||||
onMaterialsChange={handleWorkflowMaterialsChange}
|
||||
productSelectionLimit={materialProductSelectionLimit}
|
||||
theme="dark"
|
||||
totalUploadRequirement={materialTotalUploadRequirement}
|
||||
/>
|
||||
isWorkflowMaterialStepEnabled ? (
|
||||
<AgentWorkflowMaterialSelectionPanel
|
||||
appearanceRecognitionEnabled={appearanceRecognitionEnabled}
|
||||
disabled={isWorkflowPresetStepEnabled && !selectedAgentPresetId}
|
||||
inputs={workflowInputs}
|
||||
materials={workflowMaterials}
|
||||
modelImageRequirement={materialModelImageRequirement}
|
||||
onAppearanceRecognitionEnabledChange={
|
||||
handleAppearanceRecognitionEnabledChange
|
||||
}
|
||||
onInputsChange={handleWorkflowInputsChange}
|
||||
onMaterialsChange={handleWorkflowMaterialsChange}
|
||||
productSelectionLimit={materialProductSelectionLimit}
|
||||
theme="dark"
|
||||
totalUploadRequirement={materialTotalUploadRequirement}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
onAgentModeStateChange={setAgentModeState}
|
||||
onClearPreset={() => setSelectedAgentPresetId(null)}
|
||||
@ -8560,17 +8581,19 @@ export default function CanvasEditorLegacyPage({ params }: PageProps) {
|
||||
onResultPreviewUrlsChange={handleAgentResultPreviewUrlsChange}
|
||||
onWorkspaceStateChange={setAgentWorkspaceState}
|
||||
presetPanel={
|
||||
<AgentPresetPanelDarkV2
|
||||
embedded
|
||||
embeddedEntityLabel="生图模式"
|
||||
onSelectPreset={setSelectedAgentPresetId}
|
||||
selectedPresetId={selectedAgentPresetId}
|
||||
/>
|
||||
isWorkflowPresetStepEnabled ? (
|
||||
<AgentPresetPanelDarkV2
|
||||
embedded
|
||||
embeddedEntityLabel="生图模式"
|
||||
onSelectPreset={setSelectedAgentPresetId}
|
||||
selectedPresetId={selectedAgentPresetId}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
projectId={agentProjectId}
|
||||
resultCollections={agentProjectResultCollections}
|
||||
selectedAgentPresetId={
|
||||
isAgentWorkspaceEnabled ? selectedAgentPresetId : null
|
||||
isAgentWorkspaceEnabled ? effectiveSelectedAgentPresetId : null
|
||||
}
|
||||
selectedBrandModel={selectedBrandModel}
|
||||
showcaseConfig={siteSettingsData?.agentWorkflowShowcaseConfig}
|
||||
@ -8580,6 +8603,7 @@ export default function CanvasEditorLegacyPage({ params }: PageProps) {
|
||||
syncGeneratedResultsToCanvas={false}
|
||||
topNavRightSlot={agentProjectNavSlot}
|
||||
workflowContext={workflowContext}
|
||||
workflowFeatureSettings={workflowPageFeatureSettings}
|
||||
// 嵌入的风格化选择面板
|
||||
workflowPromptOptimizationEnabled={appearanceRecognitionEnabled}
|
||||
workflowStylePanel={
|
||||
|
||||
@ -82,6 +82,9 @@ import {
|
||||
import {
|
||||
normalizeAgentPresetModeConfig,
|
||||
} from "@/lib/agents/preset-mode-config";
|
||||
import {
|
||||
normalizeAgentWorkflowPageFeatureSettings,
|
||||
} from "@/lib/agents/workflow-ui-config";
|
||||
import { buildAgentSceneImageRolePrompt } from "@/lib/agents/scene-image-role-prompt";
|
||||
import type { AgentPsdTemplateMode } from "@/lib/agents/psd-template-config";
|
||||
import type { Attachment, ChatMessage } from "@/lib/types";
|
||||
@ -100,6 +103,7 @@ import type {
|
||||
AgentWorkflowShowcaseConfig,
|
||||
AgentWorkflowComposerContext,
|
||||
AgentWorkflowMaterialQueryType,
|
||||
AgentWorkflowPageFeatureSettings,
|
||||
AgentWorkflowSelectedBrandModel,
|
||||
} from "@/types/agent";
|
||||
import {
|
||||
@ -116,6 +120,7 @@ interface AgentGenerationWorkspaceDarkV2Props {
|
||||
selectedAgentPresetId?: string | null;
|
||||
selectedBrandModel?: AgentWorkflowSelectedBrandModel | null;
|
||||
workflowContext?: AgentWorkflowComposerContext | null;
|
||||
workflowFeatureSettings?: AgentWorkflowPageFeatureSettings | null;
|
||||
workflowPromptOptimizationEnabled?: boolean;
|
||||
getLatestWorkflowContext?: () => AgentWorkflowComposerContext | null;
|
||||
onBack?: () => void;
|
||||
@ -955,6 +960,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
selectedAgentPresetId,
|
||||
selectedBrandModel = null,
|
||||
workflowContext = null,
|
||||
workflowFeatureSettings = null,
|
||||
workflowPromptOptimizationEnabled = true,
|
||||
getLatestWorkflowContext,
|
||||
onBack,
|
||||
@ -1018,6 +1024,16 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
}).workspaceState || createEmptyAgentGenerationProjectWorkspaceState(),
|
||||
[workspaceState]
|
||||
);
|
||||
const resolvedWorkflowFeatureSettings = useMemo(
|
||||
() => normalizeAgentWorkflowPageFeatureSettings(workflowFeatureSettings),
|
||||
[workflowFeatureSettings]
|
||||
);
|
||||
const isPresetStepEnabled = resolvedWorkflowFeatureSettings.presetStepEnabled !== false;
|
||||
const isMaterialStepEnabled = resolvedWorkflowFeatureSettings.materialStepEnabled !== false;
|
||||
const isPsdTemplateEnabled = resolvedWorkflowFeatureSettings.psdTemplateEnabled !== false;
|
||||
const requiresPresetSelection = isPresetStepEnabled;
|
||||
const isDirectModelGenerationMode = !requiresPresetSelection;
|
||||
const hasMaterialStep = isMaterialStepEnabled && Boolean(materialSelectionPanel);
|
||||
|
||||
const [input, setInput] = useState(normalizedIncomingWorkspaceState.input);
|
||||
const [attachments, setAttachments] = useState<Array<PickedAttachment | null>>(
|
||||
@ -1088,6 +1104,10 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
mode?: AgentPsdTemplateMode | null;
|
||||
resultModeType?: AgentGenerationResultModeType | null;
|
||||
}) => {
|
||||
if (!isPsdTemplateEnabled) {
|
||||
toast.info("套 PSD 模板功能已关闭");
|
||||
return;
|
||||
}
|
||||
setPsdTemplateDialogSourceUrl(
|
||||
isValidChatHttpUrl(params?.sourceImageUrl)
|
||||
? String(params?.sourceImageUrl).trim()
|
||||
@ -1103,7 +1123,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
);
|
||||
setIsPsdTemplateDialogOpen(true);
|
||||
},
|
||||
[activeResultModeType]
|
||||
[activeResultModeType, isPsdTemplateEnabled]
|
||||
);
|
||||
const normalizedDeletedResultCollections = useMemo(
|
||||
() => ({
|
||||
@ -1144,11 +1164,17 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
saveStoredAgentPendingGenerations(allItems);
|
||||
}, [chatId, pendingGeneration]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isPsdTemplateEnabled && isPsdTemplateDialogOpen) {
|
||||
closePsdTemplateDialog();
|
||||
}
|
||||
}, [closePsdTemplateDialog, isPsdTemplateDialogOpen, isPsdTemplateEnabled]);
|
||||
|
||||
const { data: historyData } = useSWR<{ messages: ChatMessage[] }>(
|
||||
rawChatId ? `/api/messages?chatId=${rawChatId}` : null,
|
||||
fetcher,
|
||||
{
|
||||
refreshInterval: selectedAgentPresetId ? 3000 : 0,
|
||||
refreshInterval: selectedAgentPresetId || isDirectModelGenerationMode ? 3000 : 0,
|
||||
revalidateOnFocus: true,
|
||||
}
|
||||
);
|
||||
@ -1228,7 +1254,9 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
: true
|
||||
: false;
|
||||
const modelSlotIndex = modelSlotEnabled ? productSlotCount : -1;
|
||||
const hasAgentUploadSlots = Boolean(selectedAgentPreset && selectedAgentPreset.type !== "text");
|
||||
const hasAgentUploadSlots = Boolean(
|
||||
hasMaterialStep && selectedAgentPreset && selectedAgentPreset.type !== "text"
|
||||
);
|
||||
const agentUploadSlots = useMemo(
|
||||
() => {
|
||||
if (!hasAgentUploadSlots) return [];
|
||||
@ -1330,6 +1358,31 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
}, [referenceImages]);
|
||||
const sceneSecondaryReferenceCount = sceneSecondaryReferenceAttachments.length;
|
||||
const sceneMessageAttachments = useMemo(() => {
|
||||
if (isDirectModelGenerationMode && hasMaterialStep) {
|
||||
const seenKeys = new Set<string>();
|
||||
return referenceImages
|
||||
.map((image) =>
|
||||
toWorkflowReferenceAttachment({
|
||||
url: image.url,
|
||||
name: image.name,
|
||||
label: image.label,
|
||||
mimeType: image.mimeType,
|
||||
assetId: image.assetId || null,
|
||||
})
|
||||
)
|
||||
.filter((attachment): attachment is PickedAttachment => {
|
||||
if (!attachment) return false;
|
||||
const key = getAttachmentIdentityKey(attachment);
|
||||
if (key && seenKeys.has(key)) {
|
||||
return false;
|
||||
}
|
||||
if (key) {
|
||||
seenKeys.add(key);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (!isSceneGenerationPreset) {
|
||||
return attachments
|
||||
.map((attachment) => sanitizeChatAttachment(attachment))
|
||||
@ -1384,7 +1437,10 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
return ordered;
|
||||
}, [
|
||||
attachments,
|
||||
hasMaterialStep,
|
||||
isDirectModelGenerationMode,
|
||||
isSceneGenerationPreset,
|
||||
referenceImages,
|
||||
scenePrimaryReferenceCount,
|
||||
sceneSecondaryReferenceAttachments,
|
||||
selectedModelAttachment,
|
||||
@ -1577,8 +1633,33 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
);
|
||||
const [activeToolStep, setActiveToolStep] = useState<ToolStepKey>("preset");
|
||||
const hasBrandModelStep = Boolean(brandModelPanel) && modelSlotEnabled;
|
||||
const hasMaterialStep = Boolean(materialSelectionPanel);
|
||||
const hasWorkflowStyleStep = Boolean(workflowStylePanel) || hasBrandModelStep;
|
||||
const visibleWorkflowSectionKeys = useMemo(
|
||||
() => [
|
||||
...(isPresetStepEnabled ? ["mode"] : []),
|
||||
...(hasMaterialStep ? ["materials"] : []),
|
||||
...(hasWorkflowStyleStep ? ["style"] : []),
|
||||
"generate",
|
||||
],
|
||||
[hasMaterialStep, hasWorkflowStyleStep, isPresetStepEnabled]
|
||||
);
|
||||
const getWorkflowSectionBadge = useCallback(
|
||||
(key: "mode" | "materials" | "style" | "generate") => {
|
||||
const index = visibleWorkflowSectionKeys.indexOf(key);
|
||||
return String(index >= 0 ? index + 1 : visibleWorkflowSectionKeys.length + 1).padStart(
|
||||
2,
|
||||
"0"
|
||||
);
|
||||
},
|
||||
[visibleWorkflowSectionKeys]
|
||||
);
|
||||
const firstWorkflowSectionAnchor = isPresetStepEnabled
|
||||
? "agent-anchor-mode"
|
||||
: hasMaterialStep
|
||||
? "agent-anchor-materials"
|
||||
: hasWorkflowStyleStep
|
||||
? "agent-anchor-style"
|
||||
: "agent-anchor-generate";
|
||||
|
||||
const { messages, setMessages, sendMessage, status, stop } = useChat<ChatMessage>({
|
||||
id: chatId,
|
||||
@ -1591,11 +1672,15 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
prepareSendMessagesRequest(request) {
|
||||
const transportState = transportStateRef.current;
|
||||
const resolvedAgentPresetId =
|
||||
transportState.selectedAgentPresetId || selectedAgentPresetId || undefined;
|
||||
requiresPresetSelection
|
||||
? transportState.selectedAgentPresetId || selectedAgentPresetId || undefined
|
||||
: undefined;
|
||||
const resolvedSelectedModelId =
|
||||
transportState.selectedModelId !== "auto"
|
||||
? transportState.selectedModelId
|
||||
: selectedAgentPreset?.defaultModelId || transportState.selectedModelId;
|
||||
: selectedAgentPreset?.defaultModelId ||
|
||||
modelsData?.defaultModelId ||
|
||||
transportState.selectedModelId;
|
||||
const replacementModePayload =
|
||||
transportState.selectedAgentPresetType === "image" &&
|
||||
(transportState.isDetailReplacementPreset || transportState.isSkuReplacementPreset)
|
||||
@ -1652,12 +1737,14 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
MAX_CHAT_HIDDEN_PROMPT_LENGTH
|
||||
) || undefined,
|
||||
workflowPromptOptimizationEnabled,
|
||||
agentPresetId: isValidChatUuid(resolvedAgentPresetId)
|
||||
agentPresetId: requiresPresetSelection && isValidChatUuid(resolvedAgentPresetId)
|
||||
? resolvedAgentPresetId
|
||||
: undefined,
|
||||
selectedChatModel: String(resolvedSelectedModelId || "auto").trim() || "auto",
|
||||
composerMode:
|
||||
selectedAgentPreset?.type === "image" ? ("image" as const) : ("agent" as const),
|
||||
isDirectModelGenerationMode || selectedAgentPreset?.type === "image"
|
||||
? ("image" as const)
|
||||
: ("agent" as const),
|
||||
selectedVisibilityType: "private",
|
||||
imageParams: transportState.imageParams,
|
||||
contextPayload: {
|
||||
@ -2268,25 +2355,26 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
const mergedSettingsStepDescription = hasMaterialStep
|
||||
? "搜索素材、补充要求、确认槽位并开始生成"
|
||||
: "补充要求并开始生成";
|
||||
const styleStepBadge = hasMaterialStep ? "03" : "02";
|
||||
const generateStepBadge =
|
||||
hasMaterialStep && hasWorkflowStyleStep
|
||||
? "04"
|
||||
: hasMaterialStep || hasWorkflowStyleStep
|
||||
? "03"
|
||||
: "02";
|
||||
const modeStepBadge = getWorkflowSectionBadge("mode");
|
||||
const materialStepBadge = getWorkflowSectionBadge("materials");
|
||||
const styleStepBadge = getWorkflowSectionBadge("style");
|
||||
const generateStepBadge = getWorkflowSectionBadge("generate");
|
||||
|
||||
const toolSteps = useMemo(
|
||||
() => [
|
||||
{
|
||||
key: "preset" as const,
|
||||
label: "生图模式",
|
||||
description: "先选择一个模式,再继续后续配置",
|
||||
icon: Wand2,
|
||||
disabled: false,
|
||||
completed: Boolean(selectedAgentPresetId),
|
||||
optional: false,
|
||||
},
|
||||
...(isPresetStepEnabled
|
||||
? [
|
||||
{
|
||||
key: "preset" as const,
|
||||
label: "生图模式",
|
||||
description: "先选择一个模式,再继续后续配置",
|
||||
icon: Wand2,
|
||||
disabled: false,
|
||||
completed: Boolean(selectedAgentPresetId),
|
||||
optional: false,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(hasBrandModelStep
|
||||
? [
|
||||
{
|
||||
@ -2294,7 +2382,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
label: "品牌模特",
|
||||
description: "根据品牌选择模特图",
|
||||
icon: UserRound,
|
||||
disabled: !selectedAgentPresetId,
|
||||
disabled: requiresPresetSelection && !selectedAgentPresetId,
|
||||
completed: hasBrandModelSelection,
|
||||
optional: true,
|
||||
},
|
||||
@ -2305,9 +2393,9 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
label: mergedSettingsStepLabel,
|
||||
description: mergedSettingsStepDescription,
|
||||
icon: Settings2,
|
||||
disabled: !selectedAgentPresetId,
|
||||
disabled: requiresPresetSelection && !selectedAgentPresetId,
|
||||
completed:
|
||||
Boolean(selectedAgentPresetId) &&
|
||||
(!requiresPresetSelection || Boolean(selectedAgentPresetId)) &&
|
||||
(Boolean(input.trim()) || hasMaterialSelections || hasSatisfiedSlotRequirement),
|
||||
optional: false,
|
||||
},
|
||||
@ -2317,10 +2405,12 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
hasBrandModelStep,
|
||||
hasMaterialSelections,
|
||||
hasSatisfiedSlotRequirement,
|
||||
isPresetStepEnabled,
|
||||
mergedSettingsStepDescription,
|
||||
mergedSettingsStepLabel,
|
||||
hasMaterialStep,
|
||||
input,
|
||||
requiresPresetSelection,
|
||||
selectedAgentPresetId,
|
||||
]
|
||||
);
|
||||
@ -2492,7 +2582,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
}, [hasAgentUploadSlots, selectedAgentPresetId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedAgentPresetId) {
|
||||
if (requiresPresetSelection && !selectedAgentPresetId) {
|
||||
setActiveToolStep("preset");
|
||||
return;
|
||||
}
|
||||
@ -2511,14 +2601,15 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
});
|
||||
}, [
|
||||
hasBrandModelStep,
|
||||
requiresPresetSelection,
|
||||
selectedAgentPresetId,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!toolSteps.some((step) => step.key === activeToolStep)) {
|
||||
setActiveToolStep(selectedAgentPresetId ? "settings" : "preset");
|
||||
setActiveToolStep(toolSteps[0]?.key || "settings");
|
||||
}
|
||||
}, [activeToolStep, selectedAgentPresetId, toolSteps]);
|
||||
}, [activeToolStep, toolSteps]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!syncGeneratedResultsToCanvas) {
|
||||
@ -2705,7 +2796,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
void handleRunAiDetailGeneration();
|
||||
return;
|
||||
}
|
||||
if (!selectedAgentPresetId) {
|
||||
if (requiresPresetSelection && !selectedAgentPresetId) {
|
||||
toast.error("请先选择生图模式");
|
||||
return;
|
||||
}
|
||||
@ -2725,7 +2816,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
const resolvedModelId =
|
||||
selectedModelId !== "auto"
|
||||
? selectedModelId
|
||||
: selectedAgentPreset?.defaultModelId || selectedModelId;
|
||||
: selectedAgentPreset?.defaultModelId || modelsData?.defaultModelId || selectedModelId;
|
||||
if (!resolvedModelId) {
|
||||
toast.error("当前风格化未配置模型");
|
||||
return;
|
||||
@ -2846,7 +2937,9 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
isDetailAiGenerationMethod,
|
||||
isDetailReplacementPreset,
|
||||
isSkuReplacementPreset,
|
||||
modelsData?.defaultModelId,
|
||||
requiredProductUploadCount,
|
||||
requiresPresetSelection,
|
||||
selectedAgentPreset,
|
||||
selectedAgentPresetId,
|
||||
selectableModels,
|
||||
@ -3022,12 +3115,12 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
const nextStep = toolSteps.find((step) => step.key === nextKey);
|
||||
if (!nextStep) return;
|
||||
if (nextStep.disabled) {
|
||||
toast.error("请先选择生图模式");
|
||||
toast.error(requiresPresetSelection ? "请先选择生图模式" : "当前步骤暂不可用");
|
||||
return;
|
||||
}
|
||||
setActiveToolStep(nextKey);
|
||||
},
|
||||
[toolSteps]
|
||||
[requiresPresetSelection, toolSteps]
|
||||
);
|
||||
const scrollToSection = useCallback((sectionId: string) => {
|
||||
if (
|
||||
@ -4323,9 +4416,9 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
>
|
||||
<AgentWorkflowShowcaseBanner
|
||||
config={showcaseConfig}
|
||||
actionLabel="开始选择模式"
|
||||
actionLabel={isPresetStepEnabled ? "开始选择模式" : "开始配置"}
|
||||
secondaryActionLabel={allGeneratedImages.length > 0 ? "查看结果预览" : undefined}
|
||||
onActionClick={() => scrollToSection("agent-anchor-mode")}
|
||||
onActionClick={() => scrollToSection(firstWorkflowSectionAnchor)}
|
||||
onSecondaryActionClick={() => scrollToSection("agent-anchor-results")}
|
||||
/>
|
||||
</motion.section>
|
||||
@ -4338,85 +4431,87 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
className="rounded-[28px] border border-white/12 bg-white/[0.04] p-5 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]"
|
||||
>
|
||||
<div className="space-y-6">
|
||||
<section id="agent-anchor-mode" className="scroll-mt-24 rounded-[24px] border border-white/10 bg-black/20 p-4">
|
||||
<div className="mb-4 flex flex-col gap-4 border-b border-white/10 pb-4 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="rounded-full bg-emerald-500/18 px-2.5 py-1 text-[11px] font-semibold text-emerald-200">
|
||||
01
|
||||
</span>
|
||||
<span className="text-sm font-semibold text-white">选择生图模式</span>
|
||||
</div>
|
||||
<p className="text-sm text-white/45">
|
||||
先确定本次使用的模式,并完成品牌和店铺选择,后续的商品图、品牌模特、参考图和结果展示都会随之联动。
|
||||
</p>
|
||||
{isDetailReplacementPreset || isSkuReplacementPreset ? (
|
||||
<p className="text-xs text-amber-300/80">
|
||||
{isDetailReplacementPreset
|
||||
? isDetailAiGenerationMethod
|
||||
? "当前模板已切换到 AI详情生成,系统会直接复用智能规划详情链路生成整套详情图。"
|
||||
: "当前模板属于参考图替换模式,结果区会按参考图顺序逐屏展示。"
|
||||
: "当前模板属于 SKU 替换模式,结果区会按参考图顺序一对一展示。"}
|
||||
{isPresetStepEnabled ? (
|
||||
<section id="agent-anchor-mode" className="scroll-mt-24 rounded-[24px] border border-white/10 bg-black/20 p-4">
|
||||
<div className="mb-4 flex flex-col gap-4 border-b border-white/10 pb-4 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="rounded-full bg-emerald-500/18 px-2.5 py-1 text-[11px] font-semibold text-emerald-200">
|
||||
{modeStepBadge}
|
||||
</span>
|
||||
<span className="text-sm font-semibold text-white">选择生图模式</span>
|
||||
</div>
|
||||
<p className="text-sm text-white/45">
|
||||
先确定本次使用的模式,并完成品牌和店铺选择,后续的商品图、品牌模特、参考图和结果展示都会随之联动。
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
{selectedAgentPreset ? (
|
||||
<div className="flex items-center gap-3 rounded-[20px] border border-white/10 bg-black/20 px-3 py-2">
|
||||
{selectedAgentPreset.avatarUrl ? (
|
||||
<img
|
||||
src={selectedAgentPreset.avatarUrl}
|
||||
alt={selectedAgentPreset.name}
|
||||
className="h-10 w-10 rounded-xl object-cover"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-emerald-500/18">
|
||||
<Wand2 className="h-5 w-5 text-emerald-300" />
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<div className="text-sm font-medium text-white">{selectedAgentPreset.name}</div>
|
||||
<div className="text-xs text-white/40">
|
||||
{selectedAgentPreset.type === "image" ? "生图模式" : "视频模式"}
|
||||
{isDetailReplacementPreset || isSkuReplacementPreset ? (
|
||||
<p className="text-xs text-amber-300/80">
|
||||
{isDetailReplacementPreset
|
||||
? isDetailAiGenerationMethod
|
||||
? "当前模板已切换到 AI详情生成,系统会直接复用智能规划详情链路生成整套详情图。"
|
||||
: "当前模板属于参考图替换模式,结果区会按参考图顺序逐屏展示。"
|
||||
: "当前模板属于 SKU 替换模式,结果区会按参考图顺序一对一展示。"}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
{selectedAgentPreset ? (
|
||||
<div className="flex items-center gap-3 rounded-[20px] border border-white/10 bg-black/20 px-3 py-2">
|
||||
{selectedAgentPreset.avatarUrl ? (
|
||||
<img
|
||||
src={selectedAgentPreset.avatarUrl}
|
||||
alt={selectedAgentPreset.name}
|
||||
className="h-10 w-10 rounded-xl object-cover"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-emerald-500/18">
|
||||
<Wand2 className="h-5 w-5 text-emerald-300" />
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<div className="text-sm font-medium text-white">{selectedAgentPreset.name}</div>
|
||||
<div className="text-xs text-white/40">
|
||||
{selectedAgentPreset.type === "image" ? "生图模式" : "视频模式"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{selectedAgentPreset && onClearPreset ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClearPreset}
|
||||
className="rounded-full border border-white/10 bg-white/[0.03] px-3 py-2 text-xs text-emerald-300 transition-colors hover:border-emerald-300/35 hover:text-emerald-200"
|
||||
>
|
||||
更换生图模式
|
||||
</button>
|
||||
) : null}
|
||||
) : null}
|
||||
{selectedAgentPreset && onClearPreset ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClearPreset}
|
||||
className="rounded-full border border-white/10 bg-white/[0.03] px-3 py-2 text-xs text-emerald-300 transition-colors hover:border-emerald-300/35 hover:text-emerald-200"
|
||||
>
|
||||
更换生图模式
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{presetPanel ? (
|
||||
<div className="agent-dark-scrollbar max-h-[760px] overflow-y-auto pr-1">
|
||||
{presetPanel}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex min-h-[220px] items-center justify-center rounded-[24px] border border-dashed border-white/12 bg-black/20 text-sm text-white/45">
|
||||
暂无可用的生图模式配置。
|
||||
</div>
|
||||
)}
|
||||
{presetPanel ? (
|
||||
<div className="agent-dark-scrollbar max-h-[760px] overflow-y-auto pr-1">
|
||||
{presetPanel}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex min-h-[220px] items-center justify-center rounded-[24px] border border-dashed border-white/12 bg-black/20 text-sm text-white/45">
|
||||
暂无可用的生图模式配置。
|
||||
</div>
|
||||
)}
|
||||
|
||||
{brandStorePanel ? (
|
||||
<div className="mt-4 border-t border-white/10 pt-4">
|
||||
{brandStorePanel}
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
{brandStorePanel ? (
|
||||
<div className="mt-4 border-t border-white/10 pt-4">
|
||||
{brandStorePanel}
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{hasMaterialStep ? (
|
||||
<section id="agent-anchor-materials" className="scroll-mt-24 rounded-[24px] border border-white/10 bg-black/20 p-4">
|
||||
<div className="mb-4 space-y-1 border-b border-white/10 pb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="rounded-full bg-emerald-500/18 px-2.5 py-1 text-[11px] font-semibold text-emerald-200">
|
||||
02
|
||||
{materialStepBadge}
|
||||
</span>
|
||||
<span className="text-sm font-semibold text-white">选择 SPU / SKU</span>
|
||||
</div>
|
||||
@ -4607,7 +4702,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
onClick={handleSend}
|
||||
disabled={
|
||||
isAnyGenerationRunning ||
|
||||
!selectedAgentPresetId ||
|
||||
(requiresPresetSelection && !selectedAgentPresetId) ||
|
||||
(isDetailAiGenerationMethod
|
||||
? detailGenerationValidationIssues.length > 0
|
||||
: hasAgentUploadSlots && !hasSatisfiedSlotRequirement)
|
||||
@ -5067,18 +5162,20 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
{detailGenerationExpectedCount}
|
||||
</div>
|
||||
) : null}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
openPsdTemplateDialog({
|
||||
mode: getPsdTemplateModeByResultMode(activeResultModeType),
|
||||
})
|
||||
}
|
||||
className="inline-flex items-center gap-1.5 rounded-full border border-white/10 bg-white/[0.03] px-3 py-1 text-xs text-white/72 transition-colors hover:border-white/20 hover:bg-white/[0.08] hover:text-white"
|
||||
>
|
||||
<ImagePlus className="h-3.5 w-3.5" />
|
||||
<span>套PSD</span>
|
||||
</button>
|
||||
{isPsdTemplateEnabled ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
openPsdTemplateDialog({
|
||||
mode: getPsdTemplateModeByResultMode(activeResultModeType),
|
||||
})
|
||||
}
|
||||
className="inline-flex items-center gap-1.5 rounded-full border border-white/10 bg-white/[0.03] px-3 py-1 text-xs text-white/72 transition-colors hover:border-white/20 hover:bg-white/[0.08] hover:text-white"
|
||||
>
|
||||
<ImagePlus className="h-3.5 w-3.5" />
|
||||
<span>套PSD</span>
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -5913,28 +6010,30 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
>
|
||||
<Maximize2 className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
setSelectedResultUrl(card.url!);
|
||||
openPsdTemplateDialog({
|
||||
sourceImageUrl: card.url!,
|
||||
mode: getPsdTemplateModeByResultMode(activeResultModeType),
|
||||
});
|
||||
}}
|
||||
className="absolute left-14 top-3 flex items-center gap-1 rounded-full border border-white/10 bg-black/60 px-2.5 py-1 text-[11px] text-white transition-colors hover:border-white/20"
|
||||
aria-label="套用 PSD 模板"
|
||||
>
|
||||
<ImagePlus className="h-3.5 w-3.5" />
|
||||
<span>套PSD</span>
|
||||
</button>
|
||||
{isPsdTemplateEnabled ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
setSelectedResultUrl(card.url!);
|
||||
openPsdTemplateDialog({
|
||||
sourceImageUrl: card.url!,
|
||||
mode: getPsdTemplateModeByResultMode(activeResultModeType),
|
||||
});
|
||||
}}
|
||||
className="absolute left-14 top-3 flex items-center gap-1 rounded-full border border-white/10 bg-black/60 px-2.5 py-1 text-[11px] text-white transition-colors hover:border-white/20"
|
||||
aria-label="套用 PSD 模板"
|
||||
>
|
||||
<ImagePlus className="h-3.5 w-3.5" />
|
||||
<span>套PSD</span>
|
||||
</button>
|
||||
) : null}
|
||||
<div className="pointer-events-none absolute inset-x-3 bottom-3 rounded-2xl border border-white/10 bg-black/55 px-3 py-2 text-[11px] text-white/78 backdrop-blur">
|
||||
双击查看大图
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
{!isSceneResultCard ? (
|
||||
{!isSceneResultCard && isPsdTemplateEnabled ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={(event) => {
|
||||
@ -6031,29 +6130,35 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
<Wand2 className="h-8 w-8 text-white/20" />
|
||||
</div>
|
||||
<p className="text-base font-medium text-white/60">
|
||||
{selectedAgentPresetId ? `${getResultModeLabel(activeResultModeType)}结果暂为空` : "请选择生图模式"}
|
||||
{!requiresPresetSelection || selectedAgentPresetId
|
||||
? `${getResultModeLabel(activeResultModeType)}结果暂为空`
|
||||
: "请选择生图模式"}
|
||||
</p>
|
||||
<p className="mt-2 text-sm text-white/30">
|
||||
{selectedAgentPresetId
|
||||
? "切换到对应生图模式并开始生成后,结果会保留在当前项目的对应分类中。"
|
||||
{!requiresPresetSelection || selectedAgentPresetId
|
||||
? "选择模型并开始生成后,结果会保留在当前项目的对应分类中。"
|
||||
: "请先从顶部流程线选择一个生图模式"}
|
||||
</p>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={() =>
|
||||
openPsdTemplateDialog({
|
||||
mode: getPsdTemplateModeByResultMode(activeResultModeType),
|
||||
})
|
||||
}
|
||||
className="mt-5 rounded-full border-white/10 bg-white/[0.03] px-5 text-white hover:bg-white/[0.08]"
|
||||
>
|
||||
<ImagePlus className="mr-2 h-4 w-4" />
|
||||
套PSD
|
||||
</Button>
|
||||
<p className="mt-2 text-xs text-white/35">
|
||||
可直接进入模板页,并通过手动上传图片开始套版。
|
||||
</p>
|
||||
{isPsdTemplateEnabled ? (
|
||||
<>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={() =>
|
||||
openPsdTemplateDialog({
|
||||
mode: getPsdTemplateModeByResultMode(activeResultModeType),
|
||||
})
|
||||
}
|
||||
className="mt-5 rounded-full border-white/10 bg-white/[0.03] px-5 text-white hover:bg-white/[0.08]"
|
||||
>
|
||||
<ImagePlus className="mr-2 h-4 w-4" />
|
||||
套PSD
|
||||
</Button>
|
||||
<p className="mt-2 text-xs text-white/35">
|
||||
可直接进入模板页,并通过手动上传图片开始套版。
|
||||
</p>
|
||||
</>
|
||||
) : null}
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
@ -6080,61 +6185,63 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<ClientErrorBoundary
|
||||
resetKeys={[
|
||||
isPsdTemplateDialogOpen ? "open" : "closed",
|
||||
psdTemplateDialogSourceUrl || "",
|
||||
workflowContext?.brand || "",
|
||||
workflowContext?.fifthCategory || "",
|
||||
psdTemplateDialogMode,
|
||||
]}
|
||||
fallback={
|
||||
<Dialog
|
||||
{isPsdTemplateEnabled ? (
|
||||
<ClientErrorBoundary
|
||||
resetKeys={[
|
||||
isPsdTemplateDialogOpen ? "open" : "closed",
|
||||
psdTemplateDialogSourceUrl || "",
|
||||
workflowContext?.brand || "",
|
||||
workflowContext?.fifthCategory || "",
|
||||
psdTemplateDialogMode,
|
||||
]}
|
||||
fallback={
|
||||
<Dialog
|
||||
open={isPsdTemplateDialogOpen}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
closePsdTemplateDialog();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogContent
|
||||
overlayClassName="bg-black/85 backdrop-blur-sm"
|
||||
className="max-w-lg border border-white/10 bg-[#09090b] text-white"
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle>PSD 模板暂时无法打开</DialogTitle>
|
||||
<DialogDescription>
|
||||
已拦截本次客户端异常,避免整个风格化工作台被一起打断。
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-3 text-sm leading-6 text-white/72">
|
||||
<p>请关闭弹窗后重试一次。</p>
|
||||
<p>如果仍然出现,优先检查当前品牌下的 PSD 模板配置、图层数据或字体资源是否完整。</p>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
}
|
||||
>
|
||||
<AgentPsdTemplateEditorDialog
|
||||
open={isPsdTemplateDialogOpen}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
closePsdTemplateDialog();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogContent
|
||||
overlayClassName="bg-black/85 backdrop-blur-sm"
|
||||
className="max-w-lg border border-white/10 bg-[#09090b] text-white"
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle>PSD 模板暂时无法打开</DialogTitle>
|
||||
<DialogDescription>
|
||||
已拦截本次客户端异常,避免整个风格化工作台被一起打断。
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-3 text-sm leading-6 text-white/72">
|
||||
<p>请关闭弹窗后重试一次。</p>
|
||||
<p>如果仍然出现,优先检查当前品牌下的 PSD 模板配置、图层数据或字体资源是否完整。</p>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
}
|
||||
>
|
||||
<AgentPsdTemplateEditorDialog
|
||||
open={isPsdTemplateDialogOpen}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
closePsdTemplateDialog();
|
||||
sourceImageUrl={psdTemplateDialogSourceUrl}
|
||||
brandName={workflowContext?.brand || null}
|
||||
selectedCategoryNames={[workflowContext?.fifthCategory || ""].filter(Boolean)}
|
||||
sizeInfoText={workflowContext?.summaryText || null}
|
||||
templateMode={psdTemplateDialogMode}
|
||||
onDerivedImageCreated={
|
||||
onResultPreviewUrlsChange ? handlePsdDerivedImageCreated : undefined
|
||||
}
|
||||
}}
|
||||
sourceImageUrl={psdTemplateDialogSourceUrl}
|
||||
brandName={workflowContext?.brand || null}
|
||||
selectedCategoryNames={[workflowContext?.fifthCategory || ""].filter(Boolean)}
|
||||
sizeInfoText={workflowContext?.summaryText || null}
|
||||
templateMode={psdTemplateDialogMode}
|
||||
onDerivedImageCreated={
|
||||
onResultPreviewUrlsChange ? handlePsdDerivedImageCreated : undefined
|
||||
}
|
||||
onRenderedResultCreated={
|
||||
onResultPreviewUrlsChange ? handlePsdRenderedResultCreated : undefined
|
||||
}
|
||||
/>
|
||||
</ClientErrorBoundary>
|
||||
onRenderedResultCreated={
|
||||
onResultPreviewUrlsChange ? handlePsdRenderedResultCreated : undefined
|
||||
}
|
||||
/>
|
||||
</ClientErrorBoundary>
|
||||
) : null}
|
||||
|
||||
{/* Fullscreen Preview */}
|
||||
<AnimatePresence>
|
||||
|
||||
@ -4,6 +4,7 @@ import type {
|
||||
AgentWorkflowConstraintSettings,
|
||||
AgentWorkflowFourthCategoryEntry,
|
||||
AgentWorkflowOptionUiConfig,
|
||||
AgentWorkflowPageFeatureSettings,
|
||||
AgentWorkflowStoreEntry,
|
||||
AgentWorkflowThirdCategoryEntry,
|
||||
AgentWorkflowStepKey,
|
||||
@ -25,6 +26,11 @@ export const defaultAgentWorkflowCategoryLabels: AgentWorkflowCategoryLabels = {
|
||||
export const defaultAgentWorkflowConstraintSettings: AgentWorkflowConstraintSettings = {
|
||||
crossLevelMutualExclusionEnabled: false,
|
||||
};
|
||||
export const defaultAgentWorkflowPageFeatureSettings: AgentWorkflowPageFeatureSettings = {
|
||||
presetStepEnabled: true,
|
||||
materialStepEnabled: true,
|
||||
psdTemplateEnabled: true,
|
||||
};
|
||||
|
||||
export interface AgentWorkflowBrandCategoryOption {
|
||||
value: string;
|
||||
@ -169,6 +175,7 @@ export const defaultAgentWorkflowUiConfig: AgentWorkflowUiConfig = {
|
||||
brandStoreEntries: [],
|
||||
},
|
||||
constraintSettings: defaultAgentWorkflowConstraintSettings,
|
||||
pageFeatureSettings: defaultAgentWorkflowPageFeatureSettings,
|
||||
steps: defaultSteps.map((step, index) =>
|
||||
cloneStep({
|
||||
...step,
|
||||
@ -230,6 +237,30 @@ export function normalizeAgentWorkflowConstraintSettings(
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeAgentWorkflowPageFeatureSettings(
|
||||
value: unknown
|
||||
): AgentWorkflowPageFeatureSettings {
|
||||
const record =
|
||||
value && typeof value === "object" && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: {};
|
||||
|
||||
return {
|
||||
presetStepEnabled: normalizeBoolean(
|
||||
record.presetStepEnabled ?? record.modeStepEnabled ?? record.enablePresetStep,
|
||||
defaultAgentWorkflowPageFeatureSettings.presetStepEnabled
|
||||
),
|
||||
materialStepEnabled: normalizeBoolean(
|
||||
record.materialStepEnabled ?? record.skuStepEnabled ?? record.enableMaterialStep,
|
||||
defaultAgentWorkflowPageFeatureSettings.materialStepEnabled
|
||||
),
|
||||
psdTemplateEnabled: normalizeBoolean(
|
||||
record.psdTemplateEnabled ?? record.enablePsdTemplate,
|
||||
defaultAgentWorkflowPageFeatureSettings.psdTemplateEnabled
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export function flattenAgentWorkflowSteps(
|
||||
steps: AgentWorkflowStepUiConfig[],
|
||||
depth = 1,
|
||||
@ -1036,6 +1067,13 @@ export function normalizeAgentWorkflowUiConfig(value: unknown): AgentWorkflowUiC
|
||||
crossLevelMutualExclusionEnabled: raw.crossLevelMutualExclusionEnabled,
|
||||
}
|
||||
),
|
||||
pageFeatureSettings: normalizeAgentWorkflowPageFeatureSettings(
|
||||
raw.pageFeatureSettings ?? {
|
||||
presetStepEnabled: raw.presetStepEnabled ?? raw.modeStepEnabled,
|
||||
materialStepEnabled: raw.materialStepEnabled ?? raw.skuStepEnabled,
|
||||
psdTemplateEnabled: raw.psdTemplateEnabled,
|
||||
}
|
||||
),
|
||||
steps: sanitizedSteps,
|
||||
};
|
||||
}
|
||||
@ -1056,6 +1094,12 @@ export function getAgentWorkflowConstraintSettings(
|
||||
return normalizeAgentWorkflowConstraintSettings(config?.constraintSettings);
|
||||
}
|
||||
|
||||
export function getAgentWorkflowPageFeatureSettings(
|
||||
config?: AgentWorkflowUiConfig | null
|
||||
) {
|
||||
return normalizeAgentWorkflowPageFeatureSettings(config?.pageFeatureSettings);
|
||||
}
|
||||
|
||||
export function getAgentWorkflowCategoryOptionsByBrand(params: {
|
||||
config?: AgentWorkflowUiConfig | null;
|
||||
brand?: string | null;
|
||||
|
||||
@ -105,6 +105,12 @@ export interface AgentWorkflowConstraintSettings {
|
||||
crossLevelMutualExclusionEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface AgentWorkflowPageFeatureSettings {
|
||||
presetStepEnabled: boolean;
|
||||
materialStepEnabled: boolean;
|
||||
psdTemplateEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface AgentWorkflowUiConfig {
|
||||
inputOptions?: {
|
||||
brands: string[];
|
||||
@ -117,6 +123,7 @@ export interface AgentWorkflowUiConfig {
|
||||
brandStoreEntries?: AgentWorkflowBrandStoreEntry[];
|
||||
};
|
||||
constraintSettings?: AgentWorkflowConstraintSettings;
|
||||
pageFeatureSettings?: AgentWorkflowPageFeatureSettings;
|
||||
steps: AgentWorkflowStepUiConfig[];
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user