ソースを参照

feat: 聊天窗接入markdown格式展示

zhouyuhao 7 ヶ月 前
コミット
a1a35fcae0

+ 1 - 0
package.json

@@ -32,6 +32,7 @@
     "exceljs": "^4.4.0",
     "leaflet": "^1.9.4",
     "lodash": "^4.17.21",
+    "markdown-it": "^14.1.0",
     "mitt": "^3.0.1",
     "moment": "^2.30.1",
     "moment-timezone": "^0.5.46",

+ 0 - 7
src/styles/reset.scss

@@ -8,12 +8,6 @@ span,
 applet,
 object,
 iframe,
-h1,
-h2,
-h3,
-h4,
-h5,
-h6,
 p,
 blockquote,
 pre,
@@ -26,7 +20,6 @@ cite,
 code,
 del,
 dfn,
-em,
 img,
 ins,
 kbd,

+ 25 - 21
src/views/AIRobotChat/src/AIRobotChat.vue

@@ -1,10 +1,28 @@
 <script setup lang="ts">
 import AutoResizeTextarea from './components/AutoResizeTextarea.vue'
+import LoadingDots from './components/LoadingDots.vue'
 import userBubbleLight from './image/userBubbleLight.png'
 import userBubbleDark from './image/userBubbleDark.png'
 import robotBubbleLight from './image/robotBubbleLight.png'
 import robotBubbleDark from './image/robotBubbleDark.png'
 import { useThemeStore } from '@/stores/modules/theme'
+import MarkdownIt from 'markdown-it'
+
+const md = new MarkdownIt({
+  html: true,
+  linkify: true,
+  typographer: true,
+  breaks: true
+})
+const mdContent = `
+## 测试标题
+
+这是一个 **粗体** 和 *斜体* 的例子。
+
+点击这个 [链接](https://example.com) 查看详情。
+
+`
+const renderedMessage = computed(() => md.render(mdContent))
 
 const themeStore = useThemeStore()
 const modalSize = ref('large')
@@ -172,7 +190,12 @@ const handleClose = () => {
           src="./image/icon_loading.png"
           alt=""
         />
-        {{ msg.content }}
+        <p v-if="!msg.isAnswer">{{ msg.content }}</p>
+        <div v-else>
+          <div v-html="renderedMessage" class="markdown-body"></div>
+          <LoadingDots></LoadingDots>
+          <div></div>
+        </div>
         <div class="review" v-if="msg.isShowFeedback && msg.isAnswer">
           <el-button
             v-if="msg.feedback !== 'good'"
@@ -305,7 +328,7 @@ const handleClose = () => {
         height: 30px;
         margin-top: 10px;
         padding-left: 30px;
-        padding-top: 10px;
+        padding-top: 5px;
 
         button.el-button + .el-button {
           margin-left: 0px;
@@ -423,25 +446,6 @@ const handleClose = () => {
     }
   }
 
-  // .input-area {
-  //   width: 100%;
-  //   font-size: 14px;
-  //   line-height: 21px;
-  //   padding: 4px;
-  //   resize: none;
-  //   overflow-y: hidden; // 默认不显示滚动条
-  //   height: 40px; // 初始高度(1 行)
-  //   max-height: 100px; // 最多 4 行
-  //   box-sizing: border-box;
-  //   border: none;
-  //   outline-color: #fff;
-  //   border-radius: 8px;
-  //   transition: height 0.1s ease;
-  //   &::placeholder {
-  //     color: #b5b9bf;
-  //     opacity: 1;
-  //   }
-  // }
   @keyframes loading-rotate {
     0% {
       transform: rotate(0deg);

+ 46 - 0
src/views/AIRobotChat/src/components/LoadingDots.vue

@@ -0,0 +1,46 @@
+<template>
+  <div class="loading-dots">
+    <span class="dot"></span>
+    <span class="dot"></span>
+    <span class="dot"></span>
+  </div>
+</template>
+
+<style scoped>
+.loading-dots {
+  display: flex;
+  align-items: center;
+  justify-content: flex-start;
+  gap: 6px;
+  height: 24px;
+}
+
+.dot {
+  width: 4px;
+  height: 4px;
+  border-radius: 50%;
+  background-color: var(--color-theme);
+  animation: bounce 1.4s infinite ease-in-out both;
+}
+
+.dot:nth-child(1) {
+  animation-delay: 0s;
+}
+.dot:nth-child(2) {
+  animation-delay: 0.2s;
+}
+.dot:nth-child(3) {
+  animation-delay: 0.4s;
+}
+
+@keyframes bounce {
+  0%,
+  80%,
+  100% {
+    transform: translateY(0);
+  }
+  40% {
+    transform: translateY(-4px);
+  }
+}
+</style>