Skip to content

Commit 393f83b

Browse files
committed
fix:fix review
1 parent 380acb7 commit 393f83b

5 files changed

Lines changed: 76 additions & 76 deletions

File tree

packages/canvas/DesignCanvas/src/api/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ export interface NodeAIStatus {
2222

2323
export interface NodeStatus {
2424
[key: string]: any
25-
aiStatus?: NodeAIStatus // 现在包含了所有的AI状态,包括采纳状态
2625
}
2726

2827
export interface PageState {
@@ -41,6 +40,7 @@ export interface PageState {
4140
isLock: boolean
4241
isBlock: boolean
4342
nodesStatus: Record<string, NodeStatus>
43+
aiNodesStatus: Record<string, NodeAIStatus> // AI状态独立存储,避免与nodesStatus的可见性(false)冲突
4444
loading: boolean
4545
}
4646

packages/canvas/DesignCanvas/src/api/useCanvas.ts

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const defaultPageState: PageState = {
4444
isLock: false,
4545
isBlock: false,
4646
nodesStatus: {},
47+
aiNodesStatus: {},
4748
loading: false
4849
}
4950

@@ -85,13 +86,9 @@ const rootSchema = ref([
8586
}
8687
])
8788

88-
// 初始化单个节点的AI状态
89+
// 初始化单个节点的AI状态(使用独立的aiNodesStatus,避免与nodesStatus可见性冲突)
8990
const initializeNodeAIStatus = (node: object, initialStatus: Partial<NodeAIStatus> = {}) => {
90-
if (!pageState.nodesStatus[node.id]) {
91-
pageState.nodesStatus[node.id] = {}
92-
}
93-
94-
pageState.nodesStatus[node.id].aiStatus = {
91+
pageState.aiNodesStatus[node.id] = {
9592
state: 'hidden',
9693
originalNodeData: deepClone(node),
9794
aiModifiedNodeData: undefined,
@@ -108,7 +105,7 @@ const initializeAllNodesAIStatus = () => {
108105
const traverseNodes = (nodes: any[]) => {
109106
if (!nodes) return
110107
nodes.forEach((node) => {
111-
if (node.id && !pageState.nodesStatus[node.id]?.aiStatus) {
108+
if (node.id && !pageState.aiNodesStatus[node.id]) {
112109
initializeNodeAIStatus(node)
113110
}
114111
if (Array.isArray(node.children) && node.children.length) {
@@ -277,7 +274,7 @@ const updatePageSchema = (newPageSchema: any) => {
277274

278275
// 为新增的节点初始化AI状态(已存在的不覆盖)
279276
nodesMap.value.forEach(({ node }) => {
280-
if (node.id && !pageState.nodesStatus[node.id]?.aiStatus) {
277+
if (node.id && !pageState.aiNodesStatus[node.id]) {
281278
initializeNodeAIStatus(node)
282279
}
283280
})
@@ -507,16 +504,18 @@ const operationTypeMap = {
507504
if (index > -1) {
508505
parent.children.splice(index, 1)
509506
nodesMap.value.delete(node.id)
507+
delete pageState.aiNodesStatus[node.id]
510508
}
511509

512510
let children = [...(node.children || [])]
513511

514-
// 递归清理 nodesMap
512+
// 递归清理 nodesMap 和 aiNodesStatus
515513
while (children?.length) {
516514
const len = children.length
517515
children.forEach((item) => {
518516
const nodeItem = getNode(item.id)
519517
nodesMap.value.delete(item.id)
518+
delete pageState.aiNodesStatus[item.id]
520519

521520
if (Array.isArray(nodeItem?.children) && nodeItem?.children.length) {
522521
children.push(...nodeItem.children)
@@ -747,6 +746,49 @@ const updateSchema = (data: Partial<PageSchema>) => {
747746
publish({ topic: 'schemaChange', data: {} })
748747
}
749748

749+
/**
750+
* 恢复节点子树数据并重建nodesMap
751+
* 用于AI回滚场景:恢复originalNodeData后需要同步清理/重建nodesMap
752+
* @param nodeId 要恢复的节点ID
753+
* @param restoredData 恢复后的节点数据(deepClone后的originalNodeData)
754+
*/
755+
const restoreNodeSubtree = (nodeId: string, restoredData: any) => {
756+
// 1. 收集恢复前该节点子树中的所有ID(这些是需要从nodesMap中清理的)
757+
const collectSubtreeIds = (node: any): string[] => {
758+
const ids: string[] = []
759+
if (node?.id) ids.push(node.id)
760+
if (Array.isArray(node?.children)) {
761+
node.children.forEach((child: any) => ids.push(...collectSubtreeIds(child)))
762+
}
763+
return ids
764+
}
765+
766+
const currentNode = getNode(nodeId)
767+
const oldIds = currentNode ? collectSubtreeIds(currentNode) : []
768+
// 获取当前节点的parent信息(在清理前保存)
769+
const parentEntry = nodesMap.value.get(nodeId)
770+
const parentNode = parentEntry?.parent
771+
772+
// 2. 清理旧子树的nodesMap
773+
oldIds.forEach((id) => nodesMap.value.delete(id))
774+
775+
// 3. 用恢复后的数据覆盖当前节点
776+
if (currentNode) {
777+
Object.keys(currentNode).forEach((key) => delete currentNode[key])
778+
Object.assign(currentNode, restoredData)
779+
}
780+
781+
// 4. 重建该节点自身的nodesMap条目
782+
if (currentNode && parentNode) {
783+
nodesMap.value.set(nodeId, { node: currentNode, parent: parentNode })
784+
}
785+
786+
// 5. 重建子节点的nodesMap
787+
if (Array.isArray(restoredData?.children) && restoredData.children.length && currentNode) {
788+
generateNodesMap(restoredData.children, currentNode)
789+
}
790+
}
791+
750792
export default function () {
751793
return {
752794
pageState,
@@ -782,6 +824,7 @@ export default function () {
782824
getSchema,
783825
getNodePath,
784826
updateSchema,
785-
updatePageSchema
827+
updatePageSchema,
828+
restoreNodeSubtree
786829
}
787830
}

packages/canvas/container/src/components/CanvasAction.vue

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -849,15 +849,11 @@ export default {
849849
return
850850
}
851851
852-
// 恢复画布节点schema为originalNodeData
852+
// 恢复画布节点schema为originalNodeData,同步重建nodesMap
853853
if (currentAIStatus.originalNodeData) {
854-
const node = getNode(nodeId)
855-
if (node) {
856-
// 先删除AI新增的属性,再用原始数据覆盖,确保回滚幂等
857-
Object.keys(node).forEach((key) => delete node[key])
858-
Object.assign(node, deepClone(currentAIStatus.originalNodeData))
859-
useMessage().publish({ topic: 'schemaChange', data: { nodeId } })
860-
}
854+
const { restoreNodeSubtree } = useCanvas()
855+
restoreNodeSubtree(nodeId, deepClone(currentAIStatus.originalNodeData))
856+
useMessage().publish({ topic: 'schemaChange', data: { nodeId } })
861857
}
862858
863859
// 设置aiModifiedNodeData为空

packages/canvas/container/src/composables/useAIChat.ts

Lines changed: 13 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -33,28 +33,24 @@ const updateNodeAIStatus = (nodeId: string, aiStatus: Partial<NodeAIStatus>) =>
3333
const { pageState } = useCanvas()
3434
const { publish } = useMessage()
3535

36-
if (!pageState.nodesStatus[nodeId]) {
37-
pageState.nodesStatus[nodeId] = {}
38-
}
39-
40-
if (!pageState.nodesStatus[nodeId].aiStatus) {
41-
pageState.nodesStatus[nodeId].aiStatus = {
36+
if (!pageState.aiNodesStatus[nodeId]) {
37+
pageState.aiNodesStatus[nodeId] = {
4238
state: 'hidden', // 默认隐藏
4339
aiContext: null,
4440
lastAIAction: '',
4541
aiHistory: []
4642
}
4743
}
4844

49-
Object.assign(pageState.nodesStatus[nodeId].aiStatus, aiStatus)
45+
Object.assign(pageState.aiNodesStatus[nodeId], aiStatus)
5046

5147
// 发布状态更新事件
52-
publish({ topic: 'nodeAIStatusUpdate', data: { nodeId, aiStatus: pageState.nodesStatus[nodeId].aiStatus } })
48+
publish({ topic: 'nodeAIStatusUpdate', data: { nodeId, aiStatus: pageState.aiNodesStatus[nodeId] } })
5349
}
5450

5551
const getNodeAIStatus = (nodeId: string): NodeAIStatus | null => {
5652
const { pageState } = useCanvas()
57-
return pageState.nodesStatus[nodeId]?.aiStatus || null
53+
return pageState.aiNodesStatus[nodeId] || null
5854
}
5955

6056
// 添加AI操作历史记录
@@ -130,17 +126,12 @@ const cancelNodeAIAction = (nodeId: string) => {
130126
return
131127
}
132128

133-
// 恢复画布节点schema为originalNodeData
129+
// 恢复画布节点schema为originalNodeData,同步重建nodesMap
134130
if (currentStatus.originalNodeData && currentStatus.state === 'confirm') {
135-
const { getNode } = useCanvas()
131+
const { restoreNodeSubtree } = useCanvas()
136132
const { publish } = useMessage()
137-
const node = getNode(nodeId)
138-
if (node) {
139-
// 先删除AI新增的属性,再用原始数据覆盖,确保回滚幂等
140-
Object.keys(node).forEach((key) => delete node[key])
141-
Object.assign(node, deepClone(currentStatus.originalNodeData))
142-
publish({ topic: 'schemaChange', data: { nodeId } })
143-
}
133+
restoreNodeSubtree(nodeId, deepClone(currentStatus.originalNodeData))
134+
publish({ topic: 'schemaChange', data: { nodeId } })
144135
}
145136

146137
const newState = currentStatus.chatContent ? 'chat' : 'hidden'
@@ -200,17 +191,12 @@ const rejectNodeAIModification = (nodeId: string) => {
200191
return false
201192
}
202193

203-
// 恢复画布节点schema为originalNodeData
194+
// 恢复画布节点schema为originalNodeData,同步重建nodesMap
204195
if (currentAIStatus.originalNodeData) {
205-
const { getNode } = useCanvas()
196+
const { restoreNodeSubtree } = useCanvas()
206197
const { publish } = useMessage()
207-
const node = getNode(nodeId)
208-
if (node) {
209-
// 先删除AI新增的属性,再用原始数据覆盖,确保回滚幂等
210-
Object.keys(node).forEach((key) => delete node[key])
211-
Object.assign(node, deepClone(currentAIStatus.originalNodeData))
212-
publish({ topic: 'schemaChange', data: { nodeId } })
213-
}
198+
restoreNodeSubtree(nodeId, deepClone(currentAIStatus.originalNodeData))
199+
publish({ topic: 'schemaChange', data: { nodeId } })
214200
}
215201

216202
const newState = currentAIStatus.chatContent ? 'chat' : 'hidden'
@@ -245,30 +231,6 @@ const hasNodePendingAIModification = (nodeId: string): boolean => {
245231
const aiStatus = getNodeAIStatus(nodeId)
246232
return aiStatus?.state === 'confirm'
247233
}
248-
249-
/**
250-
* 获取所有有待处理AI修改的节点ID
251-
*/
252-
const getAllNodesWithPendingAIModification = (): string[] => {
253-
const { pageState } = useCanvas()
254-
const pendingNodes: string[] = []
255-
256-
Object.entries(pageState.nodesStatus).forEach(([nodeId, status]) => {
257-
if (status.aiStatus?.state === 'confirm') {
258-
pendingNodes.push(nodeId)
259-
}
260-
})
261-
262-
return pendingNodes
263-
}
264-
265-
/**
266-
* 检查页面是否有任何待处理的AI修改
267-
*/
268-
const hasAnyPendingAIModification = (): boolean => {
269-
return getAllNodesWithPendingAIModification().length > 0
270-
}
271-
272234
// ==================== AI助手状态函数 ====================
273235

274236
// 获取当前节点是否应该显示AI聊天界面
@@ -731,8 +693,6 @@ export default function () {
731693
rejectNodeAIModification,
732694
resetNodeAIAdoptionStatus,
733695
hasNodePendingAIModification,
734-
getAllNodesWithPendingAIModification,
735-
hasAnyPendingAIModification,
736696
findJsonPatchPath,
737697
applyAIPatches,
738698
// AI聊天请求构建

packages/toolbars/save/src/js/aiSaveValidation.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ const { publish } = useMessage()
1010
* 获取所有有待处理AI修改的节点ID
1111
*/
1212
const getAllNodesWithPendingAIModification = (): string[] => {
13-
const { pageState } = useCanvas()
13+
const { pageState, getNode } = useCanvas()
1414
const pendingNodes: string[] = []
1515

16-
Object.entries(pageState.nodesStatus).forEach(([nodeId, status]) => {
17-
if (status.aiStatus?.state === 'confirm') {
16+
Object.entries(pageState.aiNodesStatus).forEach(([nodeId, status]) => {
17+
// 只统计仍存在于schema中的节点,已删除节点不再阻塞保存
18+
if (status?.state === 'confirm' && getNode(nodeId)) {
1819
pendingNodes.push(nodeId)
1920
}
2021
})
@@ -44,7 +45,7 @@ const validateBeforeSave = () => {
4445
const pendingNodeIds = getAllNodesWithPendingAIModification()
4546
const pendingNodes = pendingNodeIds.map((nodeId) => {
4647
const { pageState } = useCanvas()
47-
const aiStatus = pageState.nodesStatus[nodeId]?.aiStatus || null
48+
const aiStatus = pageState.aiNodesStatus[nodeId] || null
4849
return {
4950
nodeId,
5051
aiStatus

0 commit comments

Comments
 (0)