104104 v-if =" shouldShowAILoading"
105105 class =" ai-component"
106106 @cancel =" handleAILoadingCancel"
107- @close =" closeAIHelper"
108107 ></AILoadingDialog >
109108 <AIConfirmDialog
110109 v-if =" shouldShowAIConfirm"
@@ -250,6 +249,9 @@ export default {
250249 buildAIChatRequest
251250 } = useAIChat ()
252251
252+ // AI请求的AbortController,用于取消正在进行的请求
253+ let aiChatAbortController = null
254+
253255 const remove = () => {
254256 removeNodeById (getCurrent ().schema ? .id )
255257 }
@@ -755,7 +757,34 @@ export default {
755757 // 先进入加载状态
756758 startNodeAILoading (currentSchema .id , ' AI正在处理您的请求...' )
757759
758- const response = await chat (params)
760+ // 在AI修改节点前,先刷新originalNodeData为当前最新节点数据
761+ // 避免取消/重新生成时回滚到过期的快照,丢失用户之前的普通编辑
762+ const currentNode = getNode (currentSchema .id )
763+ if (currentNode) {
764+ const currentStatus = getNodeAIStatus (currentSchema .id )
765+ if (currentStatus) {
766+ currentStatus .originalNodeData = deepClone (currentNode)
767+ }
768+ }
769+
770+ // 创建新的AbortController用于取消请求
771+ aiChatAbortController = new AbortController ()
772+ let response
773+ try {
774+ response = await chat (params, aiChatAbortController .signal )
775+ } catch (error) {
776+ // 请求被取消时不再应用补丁
777+ if (error .name === ' AbortError' || error .name === ' CanceledError' ) {
778+ return
779+ }
780+ }
781+
782+ // 响应到达后再次检查:如果用户已经取消,不应用AI补丁
783+ const currentStatus = getNodeAIStatus (currentSchema .id )
784+ if (! currentStatus || currentStatus .state !== ' loading' ) {
785+ return
786+ }
787+
759788 // AI运行完:设置chatContent、aiModifiedNodeData,修改画布schema为AI的schema
760789 applyAIPatches (currentSchema .id , response, content)
761790 }
@@ -767,6 +796,12 @@ export default {
767796 return
768797 }
769798
799+ // 中止正在进行的AI请求
800+ if (aiChatAbortController) {
801+ aiChatAbortController .abort ()
802+ aiChatAbortController = null
803+ }
804+
770805 // 取消加载状态
771806 cancelNodeAILoading (currentSchema .id )
772807 }
@@ -789,6 +824,8 @@ export default {
789824 if (currentAIStatus .originalNodeData ) {
790825 const node = getNode (nodeId)
791826 if (node) {
827+ // 先删除AI新增的属性,再用原始数据覆盖,确保回滚幂等
828+ Object .keys (node).forEach ((key ) => delete node[key])
792829 Object .assign (node, deepClone (currentAIStatus .originalNodeData ))
793830 useMessage ().publish ({ topic: ' schemaChange' , data: { nodeId } })
794831 }
@@ -811,10 +848,23 @@ export default {
811848
812849 try {
813850 const params = await buildAIChatRequest (chatContent)
814- const response = await chat (params)
851+ // 创建新的AbortController用于重新生成的请求
852+ aiChatAbortController = new AbortController ()
853+ const response = await chat (params, aiChatAbortController .signal )
854+
855+ // 响应到达后检查是否已被取消
856+ const status = getNodeAIStatus (nodeId)
857+ if (! status || status .state !== ' loading' ) {
858+ return
859+ }
860+
815861 // AI运行完操作和 handleAIChatComplete 一样
816862 applyAIPatches (nodeId, response, chatContent)
817863 } catch (error) {
864+ // 请求被取消时不做额外处理
865+ if (error .name === ' AbortError' || error .name === ' CanceledError' ) {
866+ return
867+ }
818868 cancelNodeAILoading (nodeId)
819869 }
820870 }
0 commit comments