Skip to content

Commit 567a3b8

Browse files
committed
fix:fix review
1 parent f919edd commit 567a3b8

5 files changed

Lines changed: 64 additions & 14 deletions

File tree

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import type {
2121
ChangePropsOperation,
2222
DeleteOperation,
2323
InsertOperation,
24+
NodeAIStatus,
2425
NodeOperation,
2526
PageSchema,
2627
PageState,
@@ -475,7 +476,7 @@ const operationTypeMap = {
475476
const initChildrenAIStatus = (children: Node[]) => {
476477
children.forEach((child) => {
477478
if (child.id) {
478-
initAIStatus(child)
479+
initializeNodeAIStatus(child)
479480
}
480481
if (Array.isArray(child?.children) && child.children.length > 0) {
481482
initChildrenAIStatus(child.children)

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

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
<img src="../../assets/loading.webp" class="loading" />
66
<span class="header-title">AI操作中,您可随时取消</span>
77
</div>
8-
<svg-icon name="close" class="close-icon" @click="handleClose"></svg-icon>
98
</div>
109
<div class="ai-loading-actions-row">
1110
<div class="actions-right">
@@ -21,20 +20,15 @@ export default {
2120
components: {
2221
TinyButton
2322
},
24-
emits: ['cancel', 'close'],
23+
emits: ['cancel'],
2524
2625
setup(props, { emit }) {
2726
const handleCancel = () => {
2827
emit('cancel')
2928
}
3029
31-
const handleClose = () => {
32-
emit('close')
33-
}
34-
3530
return {
36-
handleCancel,
37-
handleClose
31+
handleCancel
3832
}
3933
}
4034
}

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

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@
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
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ const cancelNodeAIAction = (nodeId: string) => {
133133
const { publish } = useMessage()
134134
const node = getNode(nodeId)
135135
if (node) {
136+
// 先删除AI新增的属性,再用原始数据覆盖,确保回滚幂等
137+
Object.keys(node).forEach((key) => delete node[key])
136138
Object.assign(node, deepClone(currentStatus.originalNodeData))
137139
publish({ topic: 'schemaChange', data: { nodeId } })
138140
}
@@ -200,6 +202,8 @@ const rejectNodeAIModification = (nodeId: string) => {
200202
const { publish } = useMessage()
201203
const node = getNode(nodeId)
202204
if (node) {
205+
// 先删除AI新增的属性,再用原始数据覆盖,确保回滚幂等
206+
Object.keys(node).forEach((key) => delete node[key])
203207
Object.assign(node, deepClone(currentAIStatus.originalNodeData))
204208
publish({ topic: 'schemaChange', data: { nodeId } })
205209
}

packages/canvas/container/src/services/agentServices.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,16 @@ export const fetchAssets = async (appId: string) => {
5757
}
5858
}
5959

60-
export const chat = async (params) => {
60+
export const chat = async (params, signal?: AbortSignal) => {
6161
const response = await getMetaApi(META_SERVICE.Http).request({
6262
url: 'app-center/api/ai/chat',
6363
method: 'POST',
6464
headers: {
6565
'Content-Type': 'application/json',
6666
...params.headers
6767
},
68-
data: params.body
68+
data: params.body,
69+
signal
6970
})
7071

7172
return response

0 commit comments

Comments
 (0)