@@ -262,6 +262,8 @@ export default {
262262
263263 // AI请求的AbortController,用于取消正在进行的请求
264264 let aiChatAbortController = null
265+ // 递增token,用于防止并发请求的响应竞争
266+ let aiChatRequestToken = 0
265267
266268 const remove = () => {
267269 removeNodeById (getCurrent ().schema ? .id )
@@ -378,14 +380,20 @@ export default {
378380 return shouldShowNodeAILoading (currentSchema .id )
379381 })
380382
381- const showAIPopover = computed (() => {
382- const currentSchema = getCurrent ().schema
383- if (! currentSchema? .id ) {
384- return false
383+ const showAIPopover = ref (false )
384+ watch (
385+ () => {
386+ const currentSchema = getCurrent ().schema
387+ if (! currentSchema? .id ) {
388+ return false
389+ }
390+ const status = getNodeAIStatus (currentSchema? .id )
391+ return status? .state !== ' hidden' && ! status? .collapsed
392+ },
393+ (val ) => {
394+ showAIPopover .value = val
385395 }
386- const status = getNodeAIStatus (currentSchema? .id )
387- return status? .state !== ' hidden' && ! status? .collapsed
388- })
396+ )
389397
390398 // 切换AI助手显示/隐藏
391399 const openAIHelper = () => {
@@ -781,7 +789,6 @@ export default {
781789 if (! currentSchema? .id ) {
782790 return
783791 }
784- const params = await buildAIChatRequest (content)
785792
786793 // 先进入加载状态
787794 startNodeAILoading (currentSchema .id , ' AI正在处理您的请求...' )
@@ -796,26 +803,38 @@ export default {
796803 }
797804 }
798805
799- // 创建新的AbortController用于取消请求
806+ // 创建新的AbortController用于取消请求,递增token防止响应竞争
800807 aiChatAbortController = new AbortController ()
801- let response
808+ const currentToken = ++ aiChatRequestToken
809+
802810 try {
803- response = await chat (params, aiChatAbortController .signal )
811+ const params = await buildAIChatRequest (content)
812+ const response = await chat (params, aiChatAbortController .signal )
813+
814+ // 响应到达后校验token:如果有更新的请求已经发出,丢弃本次响应
815+ if (currentToken !== aiChatRequestToken) {
816+ return
817+ }
818+
819+ // 响应到达后再次检查:如果用户已经取消,不应用AI补丁
820+ const status = getNodeAIStatus (currentSchema .id )
821+ if (! status || status .state !== ' loading' ) {
822+ return
823+ }
824+
825+ // AI运行完:设置chatContent、aiModifiedNodeData,修改画布schema为AI的schema
826+ // 应用失败则取消loading,避免UI永久转圈
827+ if (! applyAIPatches (currentSchema .id , response, content)) {
828+ cancelNodeAILoading (currentSchema .id )
829+ }
804830 } catch (error) {
805831 // 请求被取消时不再应用补丁
806832 if (error .name === ' AbortError' || error .name === ' CanceledError' ) {
807833 return
808834 }
835+ // 其他错误:取消loading状态,避免UI永久转圈
836+ cancelNodeAILoading (currentSchema .id )
809837 }
810-
811- // 响应到达后再次检查:如果用户已经取消,不应用AI补丁
812- const currentStatus = getNodeAIStatus (currentSchema .id )
813- if (! currentStatus || currentStatus .state !== ' loading' ) {
814- return
815- }
816-
817- // AI运行完:设置chatContent、aiModifiedNodeData,修改画布schema为AI的schema
818- applyAIPatches (currentSchema .id , response, content)
819838 }
820839
821840 // AI加载取消处理
@@ -829,6 +848,8 @@ export default {
829848 if (aiChatAbortController) {
830849 aiChatAbortController .abort ()
831850 aiChatAbortController = null
851+ // 递增token,使任何未完成的请求响应失效
852+ aiChatRequestToken++
832853 }
833854
834855 // 取消加载状态
@@ -873,18 +894,26 @@ export default {
873894
874895 try {
875896 const params = await buildAIChatRequest (chatContent)
876- // 创建新的AbortController用于重新生成的请求
897+ // 创建新的AbortController用于重新生成的请求,递增token防止响应竞争
898+ const refreshToken = ++ aiChatRequestToken
877899 aiChatAbortController = new AbortController ()
878900 const response = await chat (params, aiChatAbortController .signal )
879901
902+ // 响应到达后校验token:如果有更新的请求已经发出,丢弃本次响应
903+ if (refreshToken !== aiChatRequestToken) {
904+ return
905+ }
906+
880907 // 响应到达后检查是否已被取消
881908 const status = getNodeAIStatus (nodeId)
882909 if (! status || status .state !== ' loading' ) {
883910 return
884911 }
885912
886- // AI运行完操作和 handleAIChatComplete 一样
887- applyAIPatches (nodeId, response, chatContent)
913+ // AI运行完操作和 handleAIChatComplete 一样,应用失败则取消loading
914+ if (! applyAIPatches (nodeId, response, chatContent)) {
915+ cancelNodeAILoading (nodeId)
916+ }
888917 } catch (error) {
889918 // 请求被取消时不做额外处理
890919 if (error .name === ' AbortError' || error .name === ' CanceledError' ) {
0 commit comments