mirror of
				https://gitee.com/hhyykk/ipms-sjy-ui.git
				synced 2025-10-31 18:28:44 +08:00 
			
		
		
		
	【优化】AI Chat 抽离 Markdown 渲染
This commit is contained in:
		| @@ -29,7 +29,7 @@ const download = { | |||||||
|   html: (data: Blob, fileName: string) => { |   html: (data: Blob, fileName: string) => { | ||||||
|     download0(data, fileName, 'text/html') |     download0(data, fileName, 'text/html') | ||||||
|   }, |   }, | ||||||
|   // 下载 Markdown 方法 |   // 下载 MarkdownView 方法 | ||||||
|   markdown: (data: Blob, fileName: string) => { |   markdown: (data: Blob, fileName: string) => { | ||||||
|     download0(data, fileName, 'text/markdown') |     download0(data, fileName, 'text/markdown') | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -107,8 +107,9 @@ | |||||||
|                   <el-text class="time">{{ formatDate(item.createTime) }}</el-text> |                   <el-text class="time">{{ formatDate(item.createTime) }}</el-text> | ||||||
|                 </div> |                 </div> | ||||||
|                 <div class="left-text-container" ref="markdownViewRef"> |                 <div class="left-text-container" ref="markdownViewRef"> | ||||||
|                   <div class="left-text markdown-view" v-html="item.content"></div> | <!--                  <div class="left-text markdown-view" v-html="item.content"></div>--> | ||||||
|                   <!--                  <mdPreview :content="item.content" :delay="false" />--> |                   <!--                  <mdPreview :content="item.content" :delay="false" />--> | ||||||
|  |                   <MarkdownView class="left-text" :content="item.content" /> | ||||||
|                 </div> |                 </div> | ||||||
|                 <div class="left-btns"> |                 <div class="left-btns"> | ||||||
|                   <div class="btn-cus" @click="noCopy(item.content)"> |                   <div class="btn-cus" @click="noCopy(item.content)"> | ||||||
| @@ -135,6 +136,7 @@ | |||||||
|                 </div> |                 </div> | ||||||
|                 <div class="right-text-container"> |                 <div class="right-text-container"> | ||||||
|                   <div class="right-text">{{ item.content }}</div> |                   <div class="right-text">{{ item.content }}</div> | ||||||
|  | <!--                  <MarkdownView class="right-text" :content="item.content" />--> | ||||||
|                 </div> |                 </div> | ||||||
|                 <div class="right-btns"> |                 <div class="right-btns"> | ||||||
|                   <div class="btn-cus" @click="noCopy(item.content)"> |                   <div class="btn-cus" @click="noCopy(item.content)"> | ||||||
| @@ -198,33 +200,17 @@ | |||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|  | import MarkdownView from '@/components/MarkdownView/index.vue' | ||||||
| import {ChatMessageApi, ChatMessageVO} from '@/api/ai/chat/message' | import {ChatMessageApi, ChatMessageVO} from '@/api/ai/chat/message' | ||||||
| import {ChatConversationApi, ChatConversationVO} from '@/api/ai/chat/conversation' | import {ChatConversationApi, ChatConversationVO} from '@/api/ai/chat/conversation' | ||||||
| import ChatConversationUpdateForm from './components/ChatConversationUpdateForm.vue' | import ChatConversationUpdateForm from './components/ChatConversationUpdateForm.vue' | ||||||
| import Role from '@/views/ai/chat/role/index.vue' | import Role from '@/views/ai/chat/role/index.vue' | ||||||
| import {formatDate} from '@/utils/formatTime' | import {formatDate} from '@/utils/formatTime' | ||||||
| import {useClipboard} from '@vueuse/core' | import {useClipboard} from '@vueuse/core' | ||||||
| // 转换 markdown |  | ||||||
| import {marked} from 'marked' |  | ||||||
| // 代码高亮 https://highlightjs.org/ |  | ||||||
| import 'highlight.js/styles/vs2015.min.css' |  | ||||||
| import hljs from 'highlight.js' |  | ||||||
|  |  | ||||||
| const route = useRoute() // 路由 | const route = useRoute() // 路由 | ||||||
| const message = useMessage() // 消息弹窗 | const message = useMessage() // 消息弹窗 | ||||||
|  |  | ||||||
| // 自定义渲染器 |  | ||||||
| const renderer = { |  | ||||||
|   code(code, language, c) { |  | ||||||
|     const highlightHtml = hljs.highlight(code, {language: language, ignoreIllegals: true}).value |  | ||||||
|     const copyHtml = `<div id="copy" data-copy='${code}' style="position: absolute; right: 10px; top: 5px; color: #fff;cursor: pointer;">复制</div>` |  | ||||||
|     return `<pre>${copyHtml}<code class="hljs">${highlightHtml}</code></pre>` |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| marked.use({ |  | ||||||
|   renderer: renderer |  | ||||||
| }) |  | ||||||
|  |  | ||||||
| const conversationList = ref([] as ChatConversationVO[]) | const conversationList = ref([] as ChatConversationVO[]) | ||||||
| const conversationMap = ref<any>({}) | const conversationMap = ref<any>({}) | ||||||
| // 初始化 copy 到粘贴板 | // 初始化 copy 到粘贴板 | ||||||
| @@ -376,7 +362,7 @@ const doSendStream = async (userMessage: ChatMessageVO) => { | |||||||
|           // debugger |           // debugger | ||||||
|           content = content + data.receive.content |           content = content + data.receive.content | ||||||
|           const lastMessage = list.value[list.value.length - 1] |           const lastMessage = list.value[list.value.length - 1] | ||||||
|           lastMessage.content = marked(content) as unknown as string |           lastMessage.content = content | ||||||
|           list.value[list.value - 1] = lastMessage |           list.value[list.value - 1] = lastMessage | ||||||
|         } |         } | ||||||
|         // 滚动到最下面 |         // 滚动到最下面 | ||||||
| @@ -404,21 +390,11 @@ const doSendStream = async (userMessage: ChatMessageVO) => { | |||||||
| /** 查询列表 */ | /** 查询列表 */ | ||||||
| const messageList = async () => { | const messageList = async () => { | ||||||
|   try { |   try { | ||||||
|     if (!conversationId.value) { |     if (conversationId.value === null) { | ||||||
|       return |       return | ||||||
|     } |     } | ||||||
|     // 获取列表数据 |     // 获取列表数据 | ||||||
|     const res = await ChatMessageApi.messageList(conversationId.value) |     const res = await ChatMessageApi.messageList(conversationId.value) | ||||||
|  |  | ||||||
|     // 处理 markdown |  | ||||||
|     // marked(this.markdownText) |  | ||||||
|     res.map((item) => { |  | ||||||
|       // item.content = marked(item.content) |  | ||||||
|       if (item.type !== 'user') { |  | ||||||
|         item.content = marked(item.content) |  | ||||||
|       } |  | ||||||
|     }) |  | ||||||
|  |  | ||||||
|     list.value = res |     list.value = res | ||||||
|  |  | ||||||
|     // 滚动到最下面 |     // 滚动到最下面 | ||||||
| @@ -961,157 +937,3 @@ onMounted(async () => { | |||||||
|   } |   } | ||||||
| } | } | ||||||
| </style> | </style> | ||||||
|  |  | ||||||
| <style lang="scss"> |  | ||||||
| .markdown-view { |  | ||||||
|   font-family: PingFang SC; |  | ||||||
|   font-size: 0.95rem; |  | ||||||
|   font-weight: 400; |  | ||||||
|   line-height: 1.6rem; |  | ||||||
|   letter-spacing: 0em; |  | ||||||
|   text-align: left; |  | ||||||
|   color: #3b3e55; |  | ||||||
|   max-width: 100%; |  | ||||||
|  |  | ||||||
|   pre { |  | ||||||
|     position: relative; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   pre code.hljs { |  | ||||||
|     width: auto; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   code.hljs { |  | ||||||
|     border-radius: 6px; |  | ||||||
|     padding-top: 20px; |  | ||||||
|     width: auto; |  | ||||||
|     @media screen and (min-width: 1536px) { |  | ||||||
|       width: 960px; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @media screen and (max-width: 1536px) and (min-width: 1024px) { |  | ||||||
|       width: calc(100vw - 400px - 64px - 32px * 2); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @media screen and (max-width: 1024px) and (min-width: 768px) { |  | ||||||
|       width: calc(100vw - 32px * 2); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @media screen and (max-width: 768px) { |  | ||||||
|       width: calc(100vw - 16px * 2); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   p, |  | ||||||
|   code.hljs { |  | ||||||
|     margin-bottom: 16px; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   p { |  | ||||||
|     //margin-bottom: 1rem !important; |  | ||||||
|     margin: 0; |  | ||||||
|     margin-bottom: 3px; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /* 标题通用格式 */ |  | ||||||
|   h1, |  | ||||||
|   h2, |  | ||||||
|   h3, |  | ||||||
|   h4, |  | ||||||
|   h5, |  | ||||||
|   h6 { |  | ||||||
|     color: var(--color-G900); |  | ||||||
|     margin: 24px 0 8px; |  | ||||||
|     font-weight: 600; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   h1 { |  | ||||||
|     font-size: 22px; |  | ||||||
|     line-height: 32px; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   h2 { |  | ||||||
|     font-size: 20px; |  | ||||||
|     line-height: 30px; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   h3 { |  | ||||||
|     font-size: 18px; |  | ||||||
|     line-height: 28px; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   h4 { |  | ||||||
|     font-size: 16px; |  | ||||||
|     line-height: 26px; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   h5 { |  | ||||||
|     font-size: 16px; |  | ||||||
|     line-height: 24px; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   h6 { |  | ||||||
|     font-size: 16px; |  | ||||||
|     line-height: 24px; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /* 列表(有序,无序) */ |  | ||||||
|   ul, |  | ||||||
|   ol { |  | ||||||
|     margin: 0 0 8px 0; |  | ||||||
|     padding: 0; |  | ||||||
|     font-size: 16px; |  | ||||||
|     line-height: 24px; |  | ||||||
|     color: #3b3e55; // var(--color-CG600); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   li { |  | ||||||
|     margin: 4px 0 0 20px; |  | ||||||
|     margin-bottom: 1rem; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   ol > li { |  | ||||||
|     list-style-type: decimal; |  | ||||||
|     margin-bottom: 1rem; |  | ||||||
|     // 表达式,修复有序列表序号展示不全的问题 |  | ||||||
|     // &:nth-child(n + 10) { |  | ||||||
|     //     margin-left: 30px; |  | ||||||
|     // } |  | ||||||
|  |  | ||||||
|     // &:nth-child(n + 100) { |  | ||||||
|     //     margin-left: 30px; |  | ||||||
|     // } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   ul > li { |  | ||||||
|     list-style-type: disc; |  | ||||||
|     font-size: 16px; |  | ||||||
|     line-height: 24px; |  | ||||||
|     margin-right: 11px; |  | ||||||
|     margin-bottom: 1rem; |  | ||||||
|     color: #3b3e55; // var(--color-G900); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   ol ul, |  | ||||||
|   ol ul > li, |  | ||||||
|   ul ul, |  | ||||||
|   ul ul li { |  | ||||||
|     // list-style: circle; |  | ||||||
|     font-size: 16px; |  | ||||||
|     list-style: none; |  | ||||||
|     margin-left: 6px; |  | ||||||
|     margin-bottom: 1rem; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   ul ul ul, |  | ||||||
|   ul ul ul li, |  | ||||||
|   ol ol, |  | ||||||
|   ol ol > li, |  | ||||||
|   ol ul ul, |  | ||||||
|   ol ul ul > li, |  | ||||||
|   ul ol, |  | ||||||
|   ul ol > li { |  | ||||||
|     list-style: square; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| </style> |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 cherishsince
					cherishsince