Prechádzať zdrojové kódy

[Mod 0000]AI助手部分警告代码修复以及对话导航条逻辑修改

wangkeyi 2 týždňov pred
rodič
commit
e7e89214b6

+ 0 - 1
src/views/ventAI/manageAssistent/components/AiAssistantModal.vue

@@ -293,7 +293,6 @@
       currentTodos.value = [];
     },
     setCurrentSessionId: (id) => setCurrentSessionId(id),
-    streamErrorMessage: StreamError,
   });
 
   // —— 发送/流式管线已下沉到 useChatSend(handleSendMessage 作为输入框编排留在组件里) ——

+ 22 - 2
src/views/ventAI/manageAssistent/components/chat/ConversationNav.vue

@@ -71,6 +71,26 @@
       .replace(/\s+/g, ' ')
       .trim();
 
+  // toPlainText 是纯函数,但 qaPairs 在流式期间每个 token 都会重算:历史消息的正文一字未改,
+  // 却要跟着重跑上面整串正则(长会话下等于「每个 token × 全部历史正文」)。按原文记忆化,命中直接返回。
+  // 流式中的消息由调用方传 cacheable=false 排除——它每个 token 都变,写进缓存只会在 FIFO 淘汰下
+  // 把历史消息的条目一条条挤出去,反而让后续 token 全部落空。
+  const PLAIN_TEXT_CACHE_MAX = 200;
+  const plainTextCache = new Map<string, string>();
+
+  const toPlainTextCached = (md: string, cacheable = true): string => {
+    if (!cacheable) return toPlainText(md);
+    const cached = plainTextCache.get(md);
+    if (cached !== undefined) return cached;
+    const plain = toPlainText(md);
+    if (plainTextCache.size >= PLAIN_TEXT_CACHE_MAX) {
+      const oldestKey = plainTextCache.keys().next().value as string | undefined;
+      if (oldestKey !== undefined) plainTextCache.delete(oldestKey);
+    }
+    plainTextCache.set(md, plain);
+    return plain;
+  };
+
   const truncateText = (text: string, max = 50): string => (text.length > max ? `${text.slice(0, max)}…` : text);
 
   // 一轮问答 = 一条用户消息 + 其后的连续 AI 消息
@@ -80,10 +100,10 @@
     props.messages.forEach((msg, index) => {
       if (msg.type === 'user') {
         if (current) pairs.push(current);
-        current = { startIndex: index, question: truncateText(toPlainText(msg.content)), answer: '' };
+        current = { startIndex: index, question: truncateText(toPlainTextCached(msg.content)), answer: '' };
       } else {
         if (!current) current = { startIndex: index, question: '', answer: '' };
-        const part = msg.isLoading && !msg.content ? '正在生成...' : toPlainText(msg.content);
+        const part = msg.isLoading && !msg.content ? '正在生成...' : toPlainTextCached(msg.content, !msg.isLoading);
         if (part) current.answer = truncateText(current.answer ? `${current.answer} ${part}` : part);
       }
     });

+ 1 - 1
src/views/ventAI/manageAssistent/components/panels/SkillPanel.vue

@@ -56,7 +56,7 @@
 </template>
 
 <script setup lang="ts">
-  import { ref, reactive, computed, watch } from 'vue';
+  import { ref, reactive, computed } from 'vue';
   import { getSkills, toggleSkill, getSkillDetails } from '/@/views/ventAI/manageAssistent/api/client';
   import { renderMarkdown } from '/@/views/ventAI/manageAssistent/lib/index';
   import { message } from 'ant-design-vue';

+ 8 - 4
src/views/ventAI/manageAssistent/lib/exportDoc.ts

@@ -51,9 +51,13 @@ export const markdownToDocxBlob = async (markdown: string, title?: string): Prom
   const { Document, Packer, Paragraph, TextRun, HeadingLevel, Table, TableRow, TableCell, WidthType, AlignmentType, UnderlineType } =
     (await import('docx')) as any;
 
+  // 上面解构出的是 any 值,不能直接当类型名使用(TS2749),这里给出等价的类型别名
+  type DocxRun = any;
+  type DocxBlock = any;
+
   // 行内 token 递归渲染为 TextRun 序列(加粗/斜体/删除线/行内代码/链接)
-  const renderInline = (tokens: any[], style: { bold?: boolean; italics?: boolean; strike?: boolean } = {}): TextRun[] => {
-    const runs: TextRun[] = [];
+  const renderInline = (tokens: any[], style: { bold?: boolean; italics?: boolean; strike?: boolean } = {}): DocxRun[] => {
+    const runs: DocxRun[] = [];
     for (const t of tokens || []) {
       switch (t.type) {
         case 'text':
@@ -93,7 +97,7 @@ export const markdownToDocxBlob = async (markdown: string, title?: string): Prom
     return runs.length ? runs : [new TextRun({ text: '' })];
   };
 
-  const children: (Paragraph | Table)[] = [];
+  const children: DocxBlock[] = [];
 
   if (title) {
     children.push(new Paragraph({ text: title, heading: HeadingLevel.TITLE, alignment: AlignmentType.CENTER }));
@@ -134,7 +138,7 @@ export const markdownToDocxBlob = async (markdown: string, title?: string): Prom
       }
       case 'code': {
         const lines = (t.text || '').split('\n');
-        const runs: TextRun[] = [];
+        const runs: DocxRun[] = [];
         lines.forEach((line: string, i: number) => {
           runs.push(new TextRun({ text: line, font: { name: 'Consolas' } }));
           if (i < lines.length - 1) runs.push(new TextRun({ break: 1 }));