From 98ff635db3bcc1761563e1e0d129110629e65428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Tue, 18 Aug 2026 19:13:21 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=20UI=20=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E5=99=A8=E5=B7=A5=E4=BD=9C=E6=B5=81=E6=A0=A1=E9=AA=8C=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 抽取工作流步骤的前置与后置校验逻辑至独立模块 `WorkflowChecks`,替换原有冗余实现并优化状态传递。 --- .../{prerequisites.ts => requisites.ts} | 56 ++++++++++------ .../components/PrerequisiteIssues.tsx | 2 +- .../ui-editor/components/WorkflowChecks.ts | 65 +++++++++++++++++++ .../src/view/ui-editor/useUiEditorPage.ts | 55 ++++------------ .../tests/uiEditorState.test.ts | 2 +- 5 files changed, 113 insertions(+), 67 deletions(-) rename apps/ai-game-creator-shell/src/features/ui-editor/{prerequisites.ts => requisites.ts} (83%) create mode 100644 apps/ai-game-creator-shell/src/view/ui-editor/components/WorkflowChecks.ts diff --git a/apps/ai-game-creator-shell/src/features/ui-editor/prerequisites.ts b/apps/ai-game-creator-shell/src/features/ui-editor/requisites.ts similarity index 83% rename from apps/ai-game-creator-shell/src/features/ui-editor/prerequisites.ts rename to apps/ai-game-creator-shell/src/features/ui-editor/requisites.ts index 263723fac..98bb8538e 100644 --- a/apps/ai-game-creator-shell/src/features/ui-editor/prerequisites.ts +++ b/apps/ai-game-creator-shell/src/features/ui-editor/requisites.ts @@ -142,29 +142,40 @@ export function validateLayoutReviewPrerequisites( return validateLayoutGenerationPrerequisites(state); } -function statusIssue( +function layoutStatusIssue( status: StageStatus, - stage: 'layout' | 'components', ): UiEditorPrerequisiteIssue | null { - if (status === 'Pending') { - return { - code: `${stage}-pending`, - message: - stage === 'layout' - ? '存在尚未识别的节点布局' - : '存在尚未绑定的节点组件', - }; + switch (status) { + case 'Pending': + return { code: 'layout-pending', message: '存在尚未识别的节点布局' }; + case 'Blocked': + return { code: 'layout-blocked', message: '存在被阻塞的节点结果' }; + case 'Passed': + return null; + default: + return { + code: 'layout-needs-review', + message: status.NeedReview || '存在需要人工审阅的节点结果', + }; } - if (status === 'Blocked') { - return { code: `${stage}-blocked`, message: '存在被阻塞的节点结果' }; +} + +function componentStatusIssue( + status: StageStatus, +): UiEditorPrerequisiteIssue | null { + switch (status) { + case 'Pending': + return { code: 'components-pending', message: '存在尚未绑定的节点组件' }; + case 'Blocked': + return { code: 'components-blocked', message: '存在被阻塞的节点结果' }; + case 'Passed': + return null; + default: + return { + code: 'components-needs-review', + message: status.NeedReview || '存在需要人工审阅的节点结果', + }; } - if (typeof status === 'object' && 'NeedReview' in status) { - return { - code: `${stage}-needs-review`, - message: status.NeedReview || '存在需要人工审阅的节点结果', - }; - } - return null; } function visitNodes( @@ -205,7 +216,7 @@ export function validateStructureRecognitionResult( const issues: UiEditorPrerequisiteIssue[] = []; for (const tree of state.ui_trees) { visitNodes([tree.root], (node) => { - const issue = statusIssue(node.metadata.layout_status, 'layout'); + const issue = layoutStatusIssue(node.metadata.layout_status); if (issue) issues.push(issue); }); } @@ -218,7 +229,10 @@ export function validateVisualBindingResult( const issues: UiEditorPrerequisiteIssue[] = []; for (const tree of state.ui_trees) { visitNodes([tree.root], (node) => { - const issue = statusIssue(node.metadata.components_status, 'components'); + if (node.components.length == 0) { + return; + } + const issue = componentStatusIssue(node.metadata.components_status); if (issue) issues.push(issue); }); } diff --git a/apps/ai-game-creator-shell/src/view/ui-editor/components/PrerequisiteIssues.tsx b/apps/ai-game-creator-shell/src/view/ui-editor/components/PrerequisiteIssues.tsx index f8aae330f..079ef81ad 100644 --- a/apps/ai-game-creator-shell/src/view/ui-editor/components/PrerequisiteIssues.tsx +++ b/apps/ai-game-creator-shell/src/view/ui-editor/components/PrerequisiteIssues.tsx @@ -1,4 +1,4 @@ -import type { UiEditorPrerequisiteIssue } from '../../../features/ui-editor/prerequisites'; +import type { UiEditorPrerequisiteIssue } from '../../../features/ui-editor/requisites'; export function PrerequisiteIssues({ issues, diff --git a/apps/ai-game-creator-shell/src/view/ui-editor/components/WorkflowChecks.ts b/apps/ai-game-creator-shell/src/view/ui-editor/components/WorkflowChecks.ts new file mode 100644 index 000000000..54b77ef1f --- /dev/null +++ b/apps/ai-game-creator-shell/src/view/ui-editor/components/WorkflowChecks.ts @@ -0,0 +1,65 @@ +import { + type UiEditorPrerequisiteIssue, + validateAssetRecognitionPrerequisites, + validateComponentRecognitionPrerequisites, + validateLayoutReviewPrerequisites, + validateReferenceAnalysisResult, + validateStructureRecognitionResult, + validateVisualBindingResult, +} from '../../../features/ui-editor/requisites'; +import type { State } from '../../../features/ui-editor/types/State'; +import type { UiEditorStepId } from '../model'; + +export type { UiEditorPrerequisiteIssue }; + +export function prerequisiteIssuesForStep( + state: State, + step: UiEditorStepId, +): UiEditorPrerequisiteIssue[] { + switch (step) { + case 'reference-analysis': + return []; + case 'structure-recognition': + return validateComponentRecognitionPrerequisites(state); + case 'visual-binding': + return validateAssetRecognitionPrerequisites(state); + } +} + +export function postCheckIssuesForStep( + state: State, + step: UiEditorStepId, +): UiEditorPrerequisiteIssue[] { + switch (step) { + case 'reference-analysis': + return validateReferenceAnalysisResult(state); + case 'structure-recognition': + return validateStructureRecognitionResult(state); + case 'visual-binding': + return validateVisualBindingResult(state); + } +} + +export function postCheckIssuesForSave( + state: State, +): UiEditorPrerequisiteIssue[] { + return [ + ...validateReferenceAnalysisResult(state), + ...validateStructureRecognitionResult(state), + ...validateVisualBindingResult(state), + ]; +} + +export function activeStepPrerequisiteIssues( + state: State, + step: UiEditorStepId, +): UiEditorPrerequisiteIssue[] { + switch (step) { + case 'reference-analysis': + return validateComponentRecognitionPrerequisites(state); + case 'structure-recognition': + return validateAssetRecognitionPrerequisites(state); + case 'visual-binding': + return validateLayoutReviewPrerequisites(state); + } +} diff --git a/apps/ai-game-creator-shell/src/view/ui-editor/useUiEditorPage.ts b/apps/ai-game-creator-shell/src/view/ui-editor/useUiEditorPage.ts index 64203cafd..0dc84b1ab 100644 --- a/apps/ai-game-creator-shell/src/view/ui-editor/useUiEditorPage.ts +++ b/apps/ai-game-creator-shell/src/view/ui-editor/useUiEditorPage.ts @@ -9,15 +9,6 @@ import { prepareSpriteAssetBatch, } from '../../features/ui-editor/importAdapter'; import { applyMergeResult } from '../../features/ui-editor/merge'; -import { - type UiEditorPrerequisiteIssue, - validateAssetRecognitionPrerequisites, - validateComponentRecognitionPrerequisites, - validateLayoutReviewPrerequisites, - validateReferenceAnalysisResult, - validateStructureRecognitionResult, - validateVisualBindingResult, -} from '../../features/ui-editor/prerequisites'; import { applyRecognitionResult } from '../../features/ui-editor/recognition'; import { collectUiNodeIds } from '../../features/ui-editor/treeUtils'; import type { BindingDTO } from '../../features/ui-editor/types/BindingDTO'; @@ -50,6 +41,13 @@ import { createProjectResourcePreviewRequestId, createProjectResourcePreviewScopeId, } from '../../services/projectResourcePreviewTransport'; +import { + activeStepPrerequisiteIssues, + postCheckIssuesForSave, + postCheckIssuesForStep, + prerequisiteIssuesForStep, + type UiEditorPrerequisiteIssue, +} from './components/WorkflowChecks'; import { type PendingResourceRemoval, removalHasDownstreamReferences, @@ -562,24 +560,6 @@ export function useUiEditorPage( setPendingRemoval(null); } - function prerequisiteIssuesForStep(step: UiEditorStepId) { - if (step === 'reference-analysis') return []; - if (step === 'structure-recognition') { - return validateComponentRecognitionPrerequisites(editor.state); - } - return validateAssetRecognitionPrerequisites(editor.state); - } - - function postCheckIssuesForStep(step: UiEditorStepId) { - if (step === 'reference-analysis') { - return validateReferenceAnalysisResult(editor.state); - } - if (step === 'structure-recognition') { - return validateStructureRecognitionResult(editor.state); - } - return validateVisualBindingResult(editor.state); - } - function enterStep(step: UiEditorStepId) { setActiveStep(step); const index = @@ -593,8 +573,8 @@ export function useUiEditorPage( function requestStepChange(step: UiEditorStepId) { if (step === activeStep || isAiRunning) return; - const leavingIssues = postCheckIssuesForStep(activeStep); - const enteringIssues = prerequisiteIssuesForStep(step); + const leavingIssues = postCheckIssuesForStep(editor.state, activeStep); + const enteringIssues = prerequisiteIssuesForStep(editor.state, step); if (leavingIssues.length === 0 && enteringIssues.length === 0) { enterStep(step); return; @@ -613,21 +593,8 @@ export function useUiEditorPage( setPendingWorkflowStepChange(null); } - function postCheckIssuesForSave() { - return [ - ...validateReferenceAnalysisResult(editor.state), - ...validateStructureRecognitionResult(editor.state), - ...validateVisualBindingResult(editor.state), - ]; - } - function checkPrerequisites() { - const issues = - activeStep === 'reference-analysis' - ? validateComponentRecognitionPrerequisites(editor.state) - : activeStep === 'structure-recognition' - ? validateAssetRecognitionPrerequisites(editor.state) - : validateLayoutReviewPrerequisites(editor.state); + const issues = activeStepPrerequisiteIssues(editor.state, activeStep); setStatus( issues.length === 0 ? '前置数据检查通过。' @@ -988,7 +955,7 @@ export function useUiEditorPage( }, confirmStepChange, cancelStepChange: () => setPendingWorkflowStepChange(null), - postCheckIssuesForSave, + postCheckIssuesForSave: () => postCheckIssuesForSave(editor.state), checkPrerequisites, selectDesignImage, selectSprite, diff --git a/apps/ai-game-creator-shell/tests/uiEditorState.test.ts b/apps/ai-game-creator-shell/tests/uiEditorState.test.ts index 6cec46670..3f8ff0873 100644 --- a/apps/ai-game-creator-shell/tests/uiEditorState.test.ts +++ b/apps/ai-game-creator-shell/tests/uiEditorState.test.ts @@ -3,7 +3,7 @@ import { act, renderHook } from '@testing-library/react'; import { describe, expect, it } from 'vitest'; -import { validateComponentRecognitionPrerequisites } from '../src/features/ui-editor/prerequisites'; +import { validateComponentRecognitionPrerequisites } from '../src/features/ui-editor/requisites'; import type { FontAsset } from '../src/features/ui-editor/types/FontAsset'; import type { Node } from '../src/features/ui-editor/types/Node'; import type { SpriteAsset } from '../src/features/ui-editor/types/SpriteAsset';