修复 UI 编辑器保存与代码生成状态
普通保存和资源切换时清理生成提示与进行中状态。 为生成前置条件拦截补充可见错误,并修正保存确认按钮文案。 补齐 IUiDesignStateStore 测试替身的必需非 nullable generateCode 实现。 修正 Rust HTML 树分隔注释并同步更新 UI 编辑会话约定。
This commit is contained in:
@@ -18,7 +18,7 @@ pub(crate) fn render_ui_design_state_html(state: &State) -> Result<String, Strin
|
||||
let mut trees = Vec::with_capacity(state.ui_trees.len());
|
||||
for (index, tree) in state.ui_trees.iter().enumerate() {
|
||||
if index > 0 {
|
||||
trees.push("\n<!-- -------------------- -->\n".to_string());
|
||||
trees.push("\n<!-- ui-tree-separator -->\n".to_string());
|
||||
}
|
||||
trees.push(render_tree(state, tree)?.into_string());
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@ export default function UiEditorPage({
|
||||
if (!resourceId || session.save.isSaving || session.workflow.isAiRunning) {
|
||||
return;
|
||||
}
|
||||
setGenerateSuccess(null);
|
||||
setSaveAfterReturn(afterReturn);
|
||||
setGenerateAfterWarning(false);
|
||||
if (session.save.hasWarnings()) {
|
||||
@@ -355,7 +356,7 @@ export default function UiEditorPage({
|
||||
}
|
||||
}}
|
||||
>
|
||||
仍然保存
|
||||
{generateAfterWarning ? '仍然保存并生成' : '仍然保存'}
|
||||
</button>
|
||||
</div>
|
||||
</ThemedModal>
|
||||
|
||||
@@ -257,6 +257,8 @@ export function useUiEditorSession(
|
||||
setPersistedRevision(0);
|
||||
setSavedStateSignature(JSON.stringify(EMPTY_UI_EDITOR_STATE));
|
||||
setSaveError(null);
|
||||
setGenerateError(null);
|
||||
setIsGenerating(false);
|
||||
setHiddenNodeIds(new Set());
|
||||
return;
|
||||
}
|
||||
@@ -265,6 +267,9 @@ export function useUiEditorSession(
|
||||
setIsLoading(true);
|
||||
setLoadError(null);
|
||||
setPersistedRevision(null);
|
||||
setSaveError(null);
|
||||
setGenerateError(null);
|
||||
setIsGenerating(false);
|
||||
void stateStore
|
||||
.load(resourceId)
|
||||
.then(({ state, revision }) => {
|
||||
@@ -1048,6 +1053,7 @@ export function useUiEditorSession(
|
||||
return false;
|
||||
}
|
||||
setSaveError(null);
|
||||
setGenerateError(null);
|
||||
setIsSaving(true);
|
||||
const snapshot = structuredClone(editor.state);
|
||||
const snapshotSignature = JSON.stringify(snapshot);
|
||||
@@ -1084,6 +1090,7 @@ export function useUiEditorSession(
|
||||
persistedRevision === null ||
|
||||
editor.isLocked
|
||||
) {
|
||||
setGenerateError('当前状态不允许生成代码,请稍后重试。');
|
||||
return null;
|
||||
}
|
||||
setGenerateError(null);
|
||||
|
||||
@@ -137,6 +137,7 @@ async function renderLoadedSession(state: State) {
|
||||
const stateStore: IUiDesignStateStore = {
|
||||
load: vi.fn().mockResolvedValue({ revision: 0, state }),
|
||||
save: vi.fn(),
|
||||
generateCode: vi.fn().mockRejectedValue(new Error('测试未配置代码生成')),
|
||||
};
|
||||
const hook = renderHook(() =>
|
||||
useUiEditorSession('/tmp/ui-editor', 'ui-resource', stateStore),
|
||||
@@ -150,6 +151,7 @@ describe('UiEditorPage', () => {
|
||||
const stateStore: IUiDesignStateStore = {
|
||||
load: vi.fn().mockResolvedValue(structuredClone(EMPTY_SNAPSHOT)),
|
||||
save: vi.fn(),
|
||||
generateCode: vi.fn().mockRejectedValue(new Error('测试未配置代码生成')),
|
||||
};
|
||||
|
||||
render(
|
||||
@@ -193,6 +195,7 @@ describe('UiEditorPage', () => {
|
||||
state: stateWithPages(['page']),
|
||||
}),
|
||||
save: vi.fn(),
|
||||
generateCode: vi.fn().mockRejectedValue(new Error('测试未配置代码生成')),
|
||||
};
|
||||
const hook = renderHook(
|
||||
({ resourceId }) =>
|
||||
@@ -248,6 +251,7 @@ describe('UiEditorPage', () => {
|
||||
const stateStore: IUiDesignStateStore = {
|
||||
load: vi.fn(() => new Promise(() => undefined)),
|
||||
save: vi.fn(),
|
||||
generateCode: vi.fn().mockRejectedValue(new Error('测试未配置代码生成')),
|
||||
};
|
||||
const hook = renderHook(() =>
|
||||
useUiEditorSession('/tmp/ui-editor', 'ui-resource', stateStore),
|
||||
@@ -355,6 +359,7 @@ describe('UiEditorPage', () => {
|
||||
state: stateWithPages(['gameplay-page']),
|
||||
}),
|
||||
save: vi.fn(),
|
||||
generateCode: vi.fn().mockRejectedValue(new Error('测试未配置代码生成')),
|
||||
};
|
||||
|
||||
render(
|
||||
@@ -594,6 +599,7 @@ describe('UiEditorPage', () => {
|
||||
const stateStore: IUiDesignStateStore = {
|
||||
load: vi.fn().mockResolvedValue(structuredClone(EMPTY_SNAPSHOT)),
|
||||
save: vi.fn().mockRejectedValue(new Error('临时存储不可用')),
|
||||
generateCode: vi.fn().mockRejectedValue(new Error('测试未配置代码生成')),
|
||||
};
|
||||
render(
|
||||
createElement(UiEditorPage, {
|
||||
@@ -627,6 +633,7 @@ describe('UiEditorPage', () => {
|
||||
status: 'conflict',
|
||||
currentRevision: 1,
|
||||
}),
|
||||
generateCode: vi.fn().mockRejectedValue(new Error('测试未配置代码生成')),
|
||||
};
|
||||
render(
|
||||
createElement(UiEditorPage, {
|
||||
|
||||
@@ -16,3 +16,7 @@
|
||||
预览树在会话边界归一化为 `UITree | null`:尚未选择界面图、正在加载或尚未识别树时均以 `null` 表示,不能把 `Array.find` 的 `undefined` 传播到视图接口。
|
||||
|
||||
保存与代码生成共享同一份持久化 State/revision。会话层在保存或生成进行期间互斥拦截,且代码生成必须基于已加载的持久化 revision;视图层的保存按钮和“保存并返回”按钮同步遵守该互斥状态。
|
||||
|
||||
`UiDesignStateStore` 的 `generateCode(assetId)` 是必需能力,返回成功结果时不得为 nullable;所有注入的 adapter 与测试替身都必须实现该方法。
|
||||
|
||||
资源切换时,会话必须清理上一资源的保存/生成错误和生成中状态;普通保存开始时也清理代码生成错误。生成请求若因加载、锁定或 revision 等前置条件被拦截,必须向视图提供可见错误,而不是静默返回。
|
||||
|
||||
Reference in New Issue
Block a user