From 836431788a3b84abede8cf933f954be82cfd587b Mon Sep 17 00:00:00 2001 From: zhangyahui Date: Thu, 2 Apr 2026 10:05:29 +0800 Subject: [PATCH] =?UTF-8?q?=20feat:=20=E7=94=A8=E7=BC=93=E5=AD=98=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=AF=B9=E8=AF=9D=E4=B8=AD=E7=9A=84=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../manager/AISelectboxToolManager.ts | 8 +- .../DepthCanvas/manager/CanvasManager.ts | 18 ++-- .../DepthCanvas/manager/LayerManager.ts | 4 +- .../DepthCanvas/manager/ObjectManager.ts | 4 +- .../DepthCanvas/manager/ShapeToolManager.ts | 8 +- .../DepthCanvas/manager/StateManager.ts | 4 +- .../components/tools/threeModel/threeTool.ts | 14 +-- .../FlowCanvas/manager/GenerateManager.ts | 6 +- .../Canvas/FlowCanvas/manager/NodeManager.ts | 2 +- .../Canvas/FlowCanvas/manager/StateManager.ts | 4 +- src/stores/agent.ts | 93 ++++++++++++++++++- src/stores/versionTree.ts | 2 +- src/views/home/agent/components/Agent.vue | 64 ++++++++++++- src/views/home/agent/components/Item.vue | 8 +- src/views/home/agent/index.vue | 20 +++- 15 files changed, 208 insertions(+), 51 deletions(-) diff --git a/src/components/Canvas/DepthCanvas/manager/AISelectboxToolManager.ts b/src/components/Canvas/DepthCanvas/manager/AISelectboxToolManager.ts index d31cf97..3e1877b 100644 --- a/src/components/Canvas/DepthCanvas/manager/AISelectboxToolManager.ts +++ b/src/components/Canvas/DepthCanvas/manager/AISelectboxToolManager.ts @@ -179,10 +179,10 @@ export class AISelectboxToolManager { mouseMoveEvent(e) { if (!this.targetObject) return; if (!this.isDragging) return; - var width = e.absolutePointer.x - this.startX - var height = e.absolutePointer.y - this.startY - var left = this.startX - var top = this.startY + let width = e.absolutePointer.x - this.startX + let height = e.absolutePointer.y - this.startY + let left = this.startX + let top = this.startY if (width < 0) { left += width width = -width diff --git a/src/components/Canvas/DepthCanvas/manager/CanvasManager.ts b/src/components/Canvas/DepthCanvas/manager/CanvasManager.ts index 703e8ee..3bee2a9 100644 --- a/src/components/Canvas/DepthCanvas/manager/CanvasManager.ts +++ b/src/components/Canvas/DepthCanvas/manager/CanvasManager.ts @@ -17,7 +17,7 @@ fabric.Object.prototype.toObject = function () { const arr = [...fabric.Object.prototype.customProperties] args.forEach(v => (Array.isArray(v) ? arr.push(...v) : arr.push(v))) if (this.fill?.source === null) { - let image = new Image() + const image = new Image() image.crossOrigin = 'anonymous' image.src = this.info?.fill?.source this.fill.source = image @@ -65,17 +65,17 @@ export class CanvasManager { } } setCanvasViewSize(options) { - var canvasViewWidth = options.canvasViewWidth || 1920 - var canvasViewHeight = options.canvasViewHeight || 1080 + const canvasViewWidth = options.canvasViewWidth || 1920 + const canvasViewHeight = options.canvasViewHeight || 1080 this.canvas.setWidth(canvasViewWidth) this.canvas.setHeight(canvasViewHeight) } /** 初始化画布 */ async initCanvas(options: CanvasInitOptions) { this.layerManager = this.stateManager.layerManager - var canvasWidth = options.canvasWidth || 750 - var canvasHeight = options.canvasHeight || 600 - var image = null; + let canvasWidth = options.canvasWidth || 750 + let canvasHeight = options.canvasHeight || 600 + let image = null; if (options.url) { await new Promise((resolve) => { fabric.Image.fromURL(options.url, async (img) => { @@ -188,8 +188,8 @@ export class CanvasManager { async updateSubLayerClipPath() { const objects = this.getObjects().filter((v: any) => v.type !== "group" && !!v.info?.id); for (let i = 0; i < objects.length; i++) { - let object = objects[i] - let path = this.getObjectById(object.info.parentId)?.clipPath + const object = objects[i] + const path = this.getObjectById(object.info.parentId)?.clipPath object.set({ clipPath: path || null }) } this.renderAll() @@ -381,7 +381,7 @@ export class CanvasManager { } /** 处理JSON为正常画布 */ processCanvasDisUrlJSON(obj: { canvas: string, images: Object }) { - var json = obj.canvas; + let json = obj.canvas; const images = obj.images || {} for (const key in images) { json = json.replace(new RegExp(key), images[key]) diff --git a/src/components/Canvas/DepthCanvas/manager/LayerManager.ts b/src/components/Canvas/DepthCanvas/manager/LayerManager.ts index a9f1102..db02e9e 100644 --- a/src/components/Canvas/DepthCanvas/manager/LayerManager.ts +++ b/src/components/Canvas/DepthCanvas/manager/LayerManager.ts @@ -64,10 +64,10 @@ export class LayerManager { getLayerById(id) { function call(arr) { for (let i = 0; i < arr.length; i++) { - let v = arr[i] + const v = arr[i] if (v.info.id === id) return v if (v.children) { - let layer = call(v.children) + const layer = call(v.children) if (layer) return layer } } diff --git a/src/components/Canvas/DepthCanvas/manager/ObjectManager.ts b/src/components/Canvas/DepthCanvas/manager/ObjectManager.ts index 3c263cb..8266181 100644 --- a/src/components/Canvas/DepthCanvas/manager/ObjectManager.ts +++ b/src/components/Canvas/DepthCanvas/manager/ObjectManager.ts @@ -220,8 +220,8 @@ export class ObjectManager { const isWidth = object.hasOwnProperty('width') const isHeight = object.hasOwnProperty('height') if (isWidth || isHeight) { - let width = isWidth ? options.width : object.width - let height = isHeight ? options.height : object.height + const width = isWidth ? options.width : object.width + const height = isHeight ? options.height : object.height if (type === "polygon") { if (object.points.length === 10) {// 五角星 options.points = getStarArr(width, height) diff --git a/src/components/Canvas/DepthCanvas/manager/ShapeToolManager.ts b/src/components/Canvas/DepthCanvas/manager/ShapeToolManager.ts index 76378e5..79a23ea 100644 --- a/src/components/Canvas/DepthCanvas/manager/ShapeToolManager.ts +++ b/src/components/Canvas/DepthCanvas/manager/ShapeToolManager.ts @@ -58,10 +58,10 @@ export class ShapeToolManager { } mouseMoveEvent(e) { if (!this.isDragging) return; - var width = e.absolutePointer.x - this.startX - var height = e.absolutePointer.y - this.startY - var left = this.startX - var top = this.startY + let width = e.absolutePointer.x - this.startX + let height = e.absolutePointer.y - this.startY + let left = this.startX + let top = this.startY if (width < 0) { left += width width = -width diff --git a/src/components/Canvas/DepthCanvas/manager/StateManager.ts b/src/components/Canvas/DepthCanvas/manager/StateManager.ts index bccf835..71ef0ce 100644 --- a/src/components/Canvas/DepthCanvas/manager/StateManager.ts +++ b/src/components/Canvas/DepthCanvas/manager/StateManager.ts @@ -89,7 +89,7 @@ export class StateManager { /** 撤回状态 */ undoState() { if (this.running.value) return - var index = this.historyIndex.value - 1 + const index = this.historyIndex.value - 1 const state = this.historyList.value[index] if (!state) return this.running.value = true @@ -103,7 +103,7 @@ export class StateManager { /** 重做状态 */ redoState() { if (this.running.value) return - var index = this.historyIndex.value + 1 + const index = this.historyIndex.value + 1 const state = this.historyList.value[index] if (!state) return this.running.value = true diff --git a/src/components/Canvas/FlowCanvas/components/tools/threeModel/threeTool.ts b/src/components/Canvas/FlowCanvas/components/tools/threeModel/threeTool.ts index 34559a0..e289a61 100644 --- a/src/components/Canvas/FlowCanvas/components/tools/threeModel/threeTool.ts +++ b/src/components/Canvas/FlowCanvas/components/tools/threeModel/threeTool.ts @@ -16,8 +16,8 @@ export const initThree = (threeDom)=>{ /** * 创建渲染器对象 */ - let width = threeDom.offsetWidth; //窗口宽度 - let height = threeDom.offsetHeight; //窗口高度 + const width = threeDom.offsetWidth; //窗口宽度 + const height = threeDom.offsetHeight; //窗口高度 const renderer = new THREE.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: true,//深度缓存 防止模型闪烁重影 @@ -35,9 +35,9 @@ export const initThree = (threeDom)=>{ // 设置渲染器大小 //环境光 - let ambient = new THREE.AmbientLight(0xffffff,.8); + const ambient = new THREE.AmbientLight(0xffffff,.8); scene.add(ambient); - let controls = new OrbitControls(camera,renderer.domElement)//监听鼠标、键盘事件; + const controls = new OrbitControls(camera,renderer.domElement)//监听鼠标、键盘事件; // controls.minDistance = 500; // 设置相机与焦点的最小距离 // controls.maxDistance = 4000; // 设置相机与焦点的最大距离 controls.mouseButtons = { @@ -166,14 +166,14 @@ export const addModel = async ( load: any ) => { await new Promise((resolve, reject) => { - var fbxLoader = new GLTFLoader(); - let drac = new DRACOLoader() + const fbxLoader = new GLTFLoader(); + const drac = new DRACOLoader() drac.setDecoderPath('/draco/') fbxLoader.setDRACOLoader(drac) fbxLoader.load(url, (obj: any) => { - let scene = obj.scene; + const scene = obj.scene; scene.traverse((child: any) => { if (child.isMesh) { // 如果是基础材质,转换为标准材质 diff --git a/src/components/Canvas/FlowCanvas/manager/GenerateManager.ts b/src/components/Canvas/FlowCanvas/manager/GenerateManager.ts index 746cef6..30ac6ef 100644 --- a/src/components/Canvas/FlowCanvas/manager/GenerateManager.ts +++ b/src/components/Canvas/FlowCanvas/manager/GenerateManager.ts @@ -27,13 +27,13 @@ export class GenerateManager { async getTasksIdImg() { clearInterval(this.getTaskIdsImgTime) this.getTaskIdsImgTime = setInterval(()=>{ - let taskIds = this.taskIds.map((item)=>item.taskId) + const taskIds = this.taskIds.map((item)=>item.taskId) getTaskidResult({taskIds}).then((rv:any)=>{ //找出成功和失败的任务 - let returnedTasks = rv.filter((item)=>item.status == 'RETURNED' || item.status == 'FAILED') + const returnedTasks = rv.filter((item)=>item.status == 'RETURNED' || item.status == 'FAILED') if(returnedTasks.length == 0)return //剔除调成功的 - let taskIds_ = JSON.parse(JSON.stringify(this.taskIds)) + const taskIds_ = JSON.parse(JSON.stringify(this.taskIds)) this.taskIds = taskIds_.filter(itemA => !returnedTasks.some(itemB => itemB.taskId === itemA.taskId) ) diff --git a/src/components/Canvas/FlowCanvas/manager/NodeManager.ts b/src/components/Canvas/FlowCanvas/manager/NodeManager.ts index 9f2b9ed..49c834f 100644 --- a/src/components/Canvas/FlowCanvas/manager/NodeManager.ts +++ b/src/components/Canvas/FlowCanvas/manager/NodeManager.ts @@ -142,7 +142,7 @@ export class NodeManager { copyNodeById(id: string) { const node = this.stateManager.getNodeById(id) - let copyNode = JSON.parse(JSON.stringify(node)) + const copyNode = JSON.parse(JSON.stringify(node)) const flowNode = this.stateManager.flowManager.getNodeById(id) if (!node) return console.warn(`${id}找不到对应节点`) if (node.data?.disableCopy) return console.warn(`${id}节点已禁用复制`) diff --git a/src/components/Canvas/FlowCanvas/manager/StateManager.ts b/src/components/Canvas/FlowCanvas/manager/StateManager.ts index 00298a0..c7875d8 100644 --- a/src/components/Canvas/FlowCanvas/manager/StateManager.ts +++ b/src/components/Canvas/FlowCanvas/manager/StateManager.ts @@ -276,7 +276,7 @@ export class StateManager { } /** 撤回状态 */ undoState() { - var index = this.historyIndex.value - 1 + const index = this.historyIndex.value - 1 const state = this.historyList.value[index] if (!state) return this.historyIndex.value = index @@ -285,7 +285,7 @@ export class StateManager { } /** 重做状态 */ redoState() { - var index = this.historyIndex.value + 1 + const index = this.historyIndex.value + 1 const state = this.historyList.value[index] if (!state) return this.historyIndex.value = index diff --git a/src/stores/agent.ts b/src/stores/agent.ts index 3fbdfe9..f5984e2 100644 --- a/src/stores/agent.ts +++ b/src/stores/agent.ts @@ -5,7 +5,6 @@ import { ref, computed } from 'vue' import { removeLocal, setLocal } from '@/utils/local' import MyEvent from '@/utils/myEvent' - // Agent 项目初始数据 store type InitialProjectData = { text: string @@ -13,13 +12,13 @@ type InitialProjectData = { type: string area: string style: string - useReport:boolean - needSuggestion:boolean + useReport: boolean + needSuggestion: boolean quoteList: Array tempImages: any[] } export const useAgentStore = defineStore('agent', () => { - const initialProjectData = ref(null) + const initialProjectData = ref(null) // 保存项目初始数据 const setInitialProjectData = (data: InitialProjectData) => { @@ -34,10 +33,94 @@ export const useAgentStore = defineStore('agent', () => { initialProjectData.value = null } + // 会话缓存管理,最多保存 10 条会话 + const conversationCache = new Map() + const MAX_CACHE_ENTRIES = 10 + const SESSION_KEY_PREFIX = 'agent_session_' + + const pruneCache = () => { + const keys = Array.from(conversationCache.keys()) + const total = conversationCache.size + if (total <= MAX_CACHE_ENTRIES) return + + for (let i = 0; i < total - MAX_CACHE_ENTRIES; i++) { + conversationCache.delete(keys[i]) + sessionStorage.removeItem(SESSION_KEY_PREFIX + keys[i]) + } + } + + const saveCacheConversation = (projectId: string, data: any) => { + if (!projectId) return + + const finalData = { + ...data, + updatedAt: Date.now() + } + + if (conversationCache.has(projectId)) { + conversationCache.delete(projectId) + } + + conversationCache.set(projectId, finalData) + try { + sessionStorage.setItem(SESSION_KEY_PREFIX + projectId, JSON.stringify(finalData)) + } catch (e) { + console.warn('saveCacheConversation sessionStorage failed', e) + } + + pruneCache() + } + + const getCacheConversation = (projectId: string) => { + if (!projectId) return null + + let data = conversationCache.get(projectId) + if (data) return data + + try { + const raw = sessionStorage.getItem(SESSION_KEY_PREFIX + projectId) + if (!raw) return null + data = JSON.parse(raw) + conversationCache.set(projectId, data) + return data + } catch (e) { + console.warn('getCacheConversation failed', e) + return null + } + } + + const clearAllCacheConversations = () => { + conversationCache.value.clear() + for (const key in window.sessionStorage) { + if (key.startsWith(SESSION_KEY_PREFIX)) { + sessionStorage.removeItem(key) + } + } + } + + const removeCacheConversation = (projectId: string) => { + if (!projectId) return + conversationCache.value.delete(projectId) + sessionStorage.removeItem(SESSION_KEY_PREFIX + projectId) + } + + const getCacheStats = () => { + return { + total: conversationCache.value.size, + keys: Array.from(conversationCache.value.keys()) + } + } + return { initialProjectData, setInitialProjectData, getInitialProjectData, - clearInitialProjectData + clearInitialProjectData, + conversationCache, + saveCacheConversation, + getCacheConversation, + removeCacheConversation, + clearAllCacheConversations, + getCacheStats } }) diff --git a/src/stores/versionTree.ts b/src/stores/versionTree.ts index b967a2c..15f538a 100644 --- a/src/stores/versionTree.ts +++ b/src/stores/versionTree.ts @@ -19,7 +19,7 @@ export const useVersionTreeStore = defineStore('versionTree', () => { }) const setNodeDetail = (v: any) => { - for(let key in v){ + for(const key in v){ state.value.nodeDetail[key] = v[key] } } diff --git a/src/views/home/agent/components/Agent.vue b/src/views/home/agent/components/Agent.vue index b0b4273..39978ef 100644 --- a/src/views/home/agent/components/Agent.vue +++ b/src/views/home/agent/components/Agent.vue @@ -80,7 +80,55 @@ { deep: true } ) - const handleReset = () => { + const saveSession = (projectId: string) => { + if (!projectId) return + agentStore.saveCacheConversation(projectId, { + messageList: messageList.value, + sketchList: sketchList.value, + params: { + ...params, + projectID: projectId + }, + isGenerating: isGenerating.value, + isPaused: isPaused.value + }) + } + + const restoreSession = (projectId: string) => { + if (!projectId) return false + const payload = agentStore.getCacheConversation(projectId) + if (!payload) return false + + messageList.value = payload.messageList || [] + sketchList.value = payload.sketchList || [] + if (payload.params) { + params.projectID = payload.params.projectID || projectId + params.message = payload.params.message || '' + params.versionID = payload.params.versionID || '' + params.configParams = payload.params.configParams || params.configParams + params.needSuggestion = payload.params.needSuggestion || false + params.useReport = payload.params.useReport || false + params.imageUrlList = payload.params.imageUrlList || [] + params.quotaUrl = payload.params.quotaUrl || [] + } + isGenerating.value = false + isPaused.value = payload.isPaused || false + return true + } + + const clearSession = (projectId: string) => { + if (!projectId) return + agentStore.removeCacheConversation(projectId) + } + + const handleReset = (force = false) => { + if (!force && params.projectID) { + saveSession(params.projectID) + } + if (force && params.projectID) { + clearSession(params.projectID) + } + messageList.value = [] sketchList.value = [] params.versionID = '' @@ -94,6 +142,7 @@ style: '' } isGenerating.value = false + isPaused.value = false } // 每次请求时创建新的 AbortController @@ -109,6 +158,9 @@ } onUnmounted(() => { + if (params.projectID) { + saveSession(params.projectID) + } abort?.abort() MyEvent.remove('resetAgent', handleReset) }) @@ -453,6 +505,9 @@ isPaused.value = true isGenerating.value = false abort?.abort() + if (params.projectID) { + saveSession(params.projectID) + } MyEvent.emit('stopChat') } @@ -543,7 +598,7 @@ thinkingText: combinedThinkingText, text: combinedContent, image_url: combinedImageUrl, - webAddress: !!webAddress ? JSON.parse(webAddress) : null, + webAddress: webAddress ? JSON.parse(webAddress) : null, isUser: false, id: result.length + 1, sessionId: sessionId @@ -627,7 +682,10 @@ } defineExpose({ - setChatInfo + setChatInfo, + saveSession, + restoreSession, + clearSession }) diff --git a/src/views/home/agent/components/Item.vue b/src/views/home/agent/components/Item.vue index 80837be..8713cb9 100644 --- a/src/views/home/agent/components/Item.vue +++ b/src/views/home/agent/components/Item.vue @@ -161,12 +161,14 @@ const quotaList = props.content.image_url ?? [] list.push(...quotaList) } + console.log(imageUrls, '---imageUrls---') + if (!imageUrls || imageUrls.length === 0) return list - imageUrls.forEach((item) => { + imageUrls?.forEach((item) => { if (typeof item === 'string') { list.push(item) - } else if (typeof item === 'object' && item.url) { - list.push(item.url) + } else if (typeof item === 'object' && item?.url) { + list.push(item?.url) } }) return list diff --git a/src/views/home/agent/index.vue b/src/views/home/agent/index.vue index 22e1f0b..2038ae1 100644 --- a/src/views/home/agent/index.vue +++ b/src/views/home/agent/index.vue @@ -131,12 +131,26 @@ watch( () => proJectId.value, - (newVal, oldVal) => { + async (newVal, oldVal) => { + if (oldVal && agentRef.value && typeof agentRef.value.saveSession === 'function') { + agentRef.value.saveSession(oldVal as string) + } + projectStore.clearProject() + if (newVal) { - handleGetProjectInfoAndHistory() + let restored = false + if (agentRef.value && typeof agentRef.value.restoreSession === 'function') { + restored = await agentRef.value.restoreSession(newVal as string) + } + + if (!restored) { + handleGetProjectInfoAndHistory() + } + + MyEvent.emit('projectChange') + } else { MyEvent.emit('projectChange') - MyEvent.emit('resetAgent') } } )