Przeglądaj źródła

添加markdown展示

yangg 6 miesięcy temu
rodzic
commit
3ca34ca577
3 zmienionych plików z 183 dodań i 23 usunięć
  1. 48 0
      package-lock.json
  2. 1 0
      package.json
  3. 134 23
      src/views/report.vue

+ 48 - 0
package-lock.json

@@ -14,6 +14,7 @@
         "dayjs": "^1.11.13",
         "less": "^4.2.1",
         "less-loader": "^12.2.0",
+        "markdown-it": "^14.1.0",
         "vue": "^3.5.13",
         "vue-router": "^4.5.0",
         "vuex": "^4.0.2"
@@ -1717,6 +1718,11 @@
         "vue": ">=3.0.3"
       }
     },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+    },
     "node_modules/array-tree-filter": {
       "version": "2.1.0",
       "resolved": "https://registry.npmmirror.com/array-tree-filter/-/array-tree-filter-2.1.0.tgz",
@@ -2422,6 +2428,14 @@
         }
       }
     },
+    "node_modules/linkify-it": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/linkify-it/-/linkify-it-5.0.0.tgz",
+      "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
+      "dependencies": {
+        "uc.micro": "^2.0.0"
+      }
+    },
     "node_modules/loader-runner": {
       "version": "4.3.0",
       "resolved": "https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.0.tgz",
@@ -2497,6 +2511,27 @@
         "node": ">=6"
       }
     },
+    "node_modules/markdown-it": {
+      "version": "14.1.0",
+      "resolved": "https://registry.npmmirror.com/markdown-it/-/markdown-it-14.1.0.tgz",
+      "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
+      "dependencies": {
+        "argparse": "^2.0.1",
+        "entities": "^4.4.0",
+        "linkify-it": "^5.0.0",
+        "mdurl": "^2.0.0",
+        "punycode.js": "^2.3.1",
+        "uc.micro": "^2.1.0"
+      },
+      "bin": {
+        "markdown-it": "bin/markdown-it.mjs"
+      }
+    },
+    "node_modules/mdurl": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/mdurl/-/mdurl-2.0.0.tgz",
+      "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="
+    },
     "node_modules/merge-stream": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -2691,6 +2726,14 @@
         "node": ">=6"
       }
     },
+    "node_modules/punycode.js": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/punycode.js/-/punycode.js-2.3.1.tgz",
+      "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/randombytes": {
       "version": "2.1.0",
       "resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz",
@@ -3049,6 +3092,11 @@
       "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
       "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
     },
+    "node_modules/uc.micro": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/uc.micro/-/uc.micro-2.1.0.tgz",
+      "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="
+    },
     "node_modules/undici-types": {
       "version": "6.20.0",
       "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.20.0.tgz",

+ 1 - 0
package.json

@@ -15,6 +15,7 @@
     "dayjs": "^1.11.13",
     "less": "^4.2.1",
     "less-loader": "^12.2.0",
+    "markdown-it": "^14.1.0",
     "vue": "^3.5.13",
     "vue-router": "^4.5.0",
     "vuex": "^4.0.2"

+ 134 - 23
src/views/report.vue

@@ -36,7 +36,7 @@
     </div>
 
     <div class="chat">
-      <!-- 欢���消息 -->
+      <!-- 欢消息 -->
       <div v-if="messages.length === 0" class="welcome-section">
         <div class="welcome-header">
           <img
@@ -150,37 +150,31 @@
                   </template>
                 </div>
               </template>
-              <div>
-                {{
-                  message.role === "assistant"
-                    ? message.displayContent
-                    : message.content
-                }}
-              </div>
+              <div
+                class="message-text"
+                v-if="message.role === 'assistant'"
+                v-html="renderMarkdown(message.displayContent)"
+              ></div>
+              <div v-else>{{ message.content }}</div>
 
               <!-- 音频控制按钮 -->
               <div
                 v-if="message.role === 'assistant' && message.audioData"
                 class="audio-controls"
               >
-                <button
-                  class="audio-btn"
-                  @click="handleAudioClick(message)"
-                >
+                <button class="audio-btn" @click="handleAudioClick(message)">
                   <!-- 只在当前消息正在播放时显示波形动画 -->
-                  <div v-if="currentPlayingId === message.id" class="wave-animation">
+                  <div
+                    v-if="currentPlayingId === message.id"
+                    class="wave-animation"
+                  >
                     <span class="wave-bar"></span>
                     <span class="wave-bar"></span>
                     <span class="wave-bar"></span>
                     <span class="wave-bar"></span>
                   </div>
                   <!-- 其他情况显示播放图标 -->
-                  <img
-                    v-else
-                    :src="playIcon"
-                    alt="播放"
-                    class="audio-icon"
-                  />
+                  <img v-else :src="playIcon" alt="播放" class="audio-icon" />
                 </button>
               </div>
             </div>
@@ -374,6 +368,14 @@ import pauseIcon from "../assets/svg/pause.svg";
 import playIcon from "../assets/svg/play.svg";
 import axios from "axios";
 import { useStore } from "vuex";
+import MarkdownIt from "markdown-it";
+
+// 初始化 markdown-it
+const md = new MarkdownIt({
+  html: true,
+  linkify: true,
+  typographer: true,
+});
 
 // 响应式状态
 const headerOpen = ref(false);
@@ -732,12 +734,12 @@ const playAudioMessage = async (audioBase64, messageId) => {
     currentPlayingId.value = messageId;
 
     // 添加事件监听
-    audio.addEventListener('ended', () => {
+    audio.addEventListener("ended", () => {
       stopAudio();
     });
 
-    audio.addEventListener('error', (e) => {
-      console.error('音频播放错误:', e);
+    audio.addEventListener("error", (e) => {
+      console.error("音频播放错误:", e);
       stopAudio();
     });
 
@@ -745,7 +747,7 @@ const playAudioMessage = async (audioBase64, messageId) => {
     await audio.play();
     isPlaying.value = true;
   } catch (error) {
-    console.error('音频播放失败:', error);
+    console.error("音频播放失败:", error);
     stopAudio();
   }
 };
@@ -764,6 +766,12 @@ const stopAudio = () => {
 onUnmounted(() => {
   stopAudio();
 });
+
+// 添加 renderMarkdown 方法
+const renderMarkdown = (content) => {
+  if (!content) return "";
+  return md.render(content);
+};
 </script>
 
 <style scoped>
@@ -1727,4 +1735,107 @@ onUnmounted(() => {
   width: 20px;
   height: 20px;
 }
+.message-text {
+  margin: 0;
+  white-space: normal; /* 确保文本会换行 */
+  word-wrap: break-word; /* 长单词或 URL 会被断开以适应容器宽度 */
+  overflow: auto;
+  font-size: 12px;
+  line-height: 20px;
+}
+.message-text ::v-deep p {
+  font-size: 12px !important;
+  margin: 5px 0 10px;
+  padding: 0;
+  font-size: inherit;
+  line-height: 22px;
+  font-weight: inherit;
+}
+.message-text ::v-deep ol {
+  padding-bottom: 10px;
+}
+.message-text ::v-deep .source-section h3 {
+  margin: 20px 0 3px;
+  color: #333;
+  font-size: 16px;
+}
+.message-text ::v-deep ol li {
+  padding-top: 3px;
+}
+.message-text ::v-deep ol li a {
+  font-size: 14px;
+  color: #1296db;
+}
+</style>
+<style lang="less" scoped>
+::v-deep .message-content {
+  /* 确保 scoped 样式能够渗透到动态生成的内容 */
+  h1,
+  h2,
+  h3,
+  h4,
+  h5,
+  h6 {
+    margin-top: 16px;
+    margin-bottom: 8px;
+    font-weight: 600;
+  }
+
+  p {
+    margin-bottom: 8px;
+  }
+
+  code {
+    background: #f5f5f5;
+    padding: 2px 4px;
+    border-radius: 4px;
+    font-family: monospace;
+  }
+
+  pre {
+    background: #f5f5f5;
+    padding: 16px;
+    border-radius: 8px;
+    overflow-x: auto;
+  }
+
+  ul,
+  ol {
+    padding-left: 24px;
+    margin-bottom: 8px;
+  }
+
+  blockquote {
+    border-left: 4px solid #ddd;
+    padding-left: 16px;
+    margin: 8px 0;
+    color: rgba(0, 0, 0, 0.65);
+  }
+
+  a {
+    color: #1677ff;
+    text-decoration: none;
+  }
+
+  a:hover {
+    text-decoration: underline;
+  }
+
+  table {
+    border-collapse: collapse;
+    width: 100%;
+    margin-bottom: 16px;
+  }
+
+  th,
+  td {
+    border: 1px solid #ddd;
+    padding: 8px;
+    text-align: left;
+  }
+
+  th {
+    background: #f5f5f5;
+  }
+}
 </style>