Переглянути джерело

[Feat 0000]AI助手新增单条任务删除功能

wangkeyi 6 днів тому
батько
коміт
d38899ab80

+ 8 - 0
src/views/ventAI/manageAssistent/api.ts

@@ -8,6 +8,7 @@ enum Api {
   getHistoryList = '/ventAI/api/sessions',
   getDetail = '/ventAI/api/chat/history/',
   deleteSession = '/ventAI/api/sessions/',
+  deleteMessage = '/ventAI/api/messages/',
   chatResume = '/ventAI/api/chat/resume',
   getModelMsg = '/ventAI/api/model',
   switchThinking = '/ventAI/api/model/thinking',
@@ -106,6 +107,13 @@ export const getDetail = (session_id: string) => defHttp.get({ url: Api.getDetai
  */
 export const deleteSession = (session_id: string) => defHttp.delete({ url: Api.deleteSession + session_id }, { isTransformResponse: false });
 
+/**
+ * 删除单条消息(按消息主键 id)
+ * 删除后服务端会清空该会话的内存线程,下轮对话从数据库重建最近 40 条历史
+ * @param message_id - 消息主键 id(来自历史详情 messages[].id)
+ */
+export const deleteMessage = (message_id: number) => defHttp.delete({ url: Api.deleteMessage + message_id }, { isTransformResponse: false });
+
 /**
  * 置顶会话
  * @param session_id - 会话ID

+ 31 - 3
src/views/ventAI/manageAssistent/components/AiAssistantModal.vue

@@ -68,6 +68,7 @@
             @ask-user-cancel="handleAskUserCancel"
             @agent-detail-click="handleAgentDetailClick"
             @open-markdown-tab="openMarkdownPreviewTab"
+            @delete-message="handleDeleteMessage"
           />
 
           <!-- 任务进度条 -->
@@ -156,6 +157,7 @@
     getHistoryList,
     getDetail,
     deleteSession,
+    deleteMessage,
     pinSession,
     editSessionTitle,
     getContext,
@@ -913,6 +915,7 @@
 
   const transformHistoryToMessages = (
     data: Array<{
+      id?: number;
       role?: string;
       type?: string;
       content: string;
@@ -931,9 +934,9 @@
 
       // 新格式:role 字段 (user/assistant)
       if (item.role === 'user') {
-        messages.push({ type: 'user', content: normalizedContent, time, createdAt });
+        messages.push({ id: item.id, type: 'user', content: normalizedContent, time, createdAt });
       } else if (item.role === 'assistant') {
-        const msg: Message = { type: 'ai', content: normalizedContent, time, createdAt };
+        const msg: Message = { id: item.id, type: 'ai', content: normalizedContent, time, createdAt };
         if (item.report_url && item.report_filename) {
           msg.downloadFiles = [{ url: item.report_url, filename: item.report_filename }];
         }
@@ -950,7 +953,7 @@
       }
       // 兼容旧格式:type 字段
       else if (item.type === 'user_message') {
-        messages.push({ type: 'user', content: normalizedContent, time, createdAt });
+        messages.push({ id: item.id, type: 'user', content: normalizedContent, time, createdAt });
       } else if (item.type === 'model_thinking') {
         const lastAiMsg = messages[messages.length - 1];
         if (lastAiMsg && lastAiMsg.type === 'ai') {
@@ -1072,6 +1075,31 @@
     });
   };
 
+  // 删除单条消息:先删数据库行,后端再清空该会话内存线程,下轮从数据库重建上下文
+  const handleDeleteMessage = (msg: Message) => {
+    if (!msg.id) return;
+    Modal.confirm({
+      title: '提示',
+      content: '删除后该消息将不再进入模型上下文,是否继续?',
+      okText: '确认',
+      cancelText: '取消',
+      wrapClassName: 'delete-task-confirm-modal',
+      onOk: async () => {
+        try {
+          await deleteMessage(msg.id!);
+          message.success('消息已删除');
+          messages.value = messages.value.filter((m) => m !== msg);
+          const task = currentTask.value;
+          if (task) {
+            task.messages = task.messages.filter((m) => m !== msg);
+          }
+        } catch {
+          message.error('删除失败');
+        }
+      },
+    });
+  };
+
   // 在右侧面板新建标签页展示 Markdown 代码块渲染效果(相同内容复用同一标签)
   const openMarkdownPreviewTab = (payload: { title: string; content: string }) => {
     let hash = 0;

+ 7 - 0
src/views/ventAI/manageAssistent/components/chatModal/ChatMessages.vue

@@ -301,6 +301,9 @@
             <div class="action-btn" @click="copyMessageContent(message.content, index)" :title="copiedMessageIndex === index ? '复制成功' : '复制'">
               <img :src="copyIconUrl(index)" width="14" height="14" />
             </div>
+            <div v-if="message.id" class="action-btn" @click="emit('delete-message', message)" title="删除消息">
+              <img :src="getCssUrl('--img-chat-delete-icon')" width="14" height="14" />
+            </div>
             <span v-if="message.createdAt" class="action-time">{{ formatFullTime(message.createdAt) }}</span>
           </div>
         </div>
@@ -322,6 +325,9 @@
             <div class="action-btn" @click="copyMessageContent(message.content, index)" :title="copiedMessageIndex === index ? '复制成功' : '复制'">
               <img :src="copyIconUrl(index)" width="14" height="14" />
             </div>
+            <div v-if="message.id" class="action-btn" @click="emit('delete-message', message)" title="删除消息">
+              <img :src="getCssUrl('--img-chat-delete-icon')" width="14" height="14" />
+            </div>
             <span v-if="message.createdAt" class="action-time">{{ formatFullTime(message.createdAt) }}</span>
           </div>
         </div>
@@ -456,6 +462,7 @@
     (e: 'ask-user-cancel'): void;
     (e: 'agent-detail-click', data: AgentDetailData): void;
     (e: 'open-markdown-tab', payload: { title: string; content: string }): void;
+    (e: 'delete-message', message: Message): void;
   }>();
 
   const isEditableFile = (filename: string) => isMdFile(filename) || isTextFile(filename);

+ 1 - 0
src/views/ventAI/manageAssistent/components/chatModal/types.ts

@@ -82,6 +82,7 @@ export interface PendingApprovalData {
 }
 
 export interface Message {
+  id?: number;
   type: 'user' | 'ai';
   content: string;
   thinkingContent?: string;