Commit 6d548eb4 authored by 水玉婷's avatar 水玉婷
Browse files

feat:添加markdown 模版

parent e32996f4
...@@ -3,7 +3,7 @@ import axios from 'axios' ...@@ -3,7 +3,7 @@ import axios from 'axios'
// 创建axios实例 // 创建axios实例
const instance = axios.create({ const instance = axios.create({
baseURL: '/', baseURL: '/',
timeout: 10000, timeout: 60000,
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
} }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
<img :src="props.logoUrl || defaultAvatar" alt="avatar" class="avatar-image" /> <img :src="props.logoUrl || defaultAvatar" alt="avatar" class="avatar-image" />
</div> </div>
<div class="header-info"> <div class="header-info">
<h2>{{ props.dialogSessionId ? props?.detailData?.title || '继续对话' : '新建对话' }}</h2> <h2>{{ props.dialogSessionId ? props?.detailData?.title || '继续对话' : '新建对话' }}</h2>
</div> </div>
</div> </div>
...@@ -88,7 +88,7 @@ import { EventSourcePolyfill } from 'event-source-polyfill'; ...@@ -88,7 +88,7 @@ import { EventSourcePolyfill } from 'event-source-polyfill';
import { ref, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'; import { ref, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { post,get} from '@/utils/axios.js'; // 导入axios的post方法 import { post,get} from '@/utils/axios.js'; // 导入axios的post方法
import tableTemplate from './tableTemplate'; import { tableTemplate } from './tableTemplate';
import { SendOutlined, UserOutlined } from '@ant-design/icons-vue'; import { SendOutlined, UserOutlined } from '@ant-design/icons-vue';
import defaultAvatar from '@/assets/logo.png'; import defaultAvatar from '@/assets/logo.png';
import ChartComponent from './ChartComponent.vue'; // 导入独立的图表组件 import ChartComponent from './ChartComponent.vue'; // 导入独立的图表组件
...@@ -248,6 +248,88 @@ const contentTemplates = { ...@@ -248,6 +248,88 @@ const contentTemplates = {
</div> </div>
<audio id="${audioId}" src="${src}" preload="metadata" style="display: none;"></audio> <audio id="${audioId}" src="${src}" preload="metadata" style="display: none;"></audio>
</div>`; </div>`;
},
// Markdown模板
markdown: (content: any) => {
// 类型检查和默认值处理
if (typeof content !== 'string') {
// 如果是对象,尝试转换为字符串
if (content && typeof content === 'object') {
content = JSON.stringify(content);
} else {
// 其他类型转换为字符串
content = String(content || '');
}
}
// 简单的Markdown解析器
const parseMarkdown = (text: string) => {
// 确保text是字符串
if (typeof text !== 'string') {
text = String(text || '');
}
// 处理标题
text = text.replace(/^### (.*$)/gim, '<h3>$1</h3>');
text = text.replace(/^## (.*$)/gim, '<h2>$1</h2>');
text = text.replace(/^# (.*$)/gim, '<h1>$1</h1>');
// 处理粗体
text = text.replace(/\*\*(.*?)\*\*/gim, '<strong>$1</strong>');
text = text.replace(/\*(.*?)\*/gim, '<em>$1</em>');
// 处理代码块
text = text.replace(/```([\s\S]*?)```/gim, '<pre><code>$1</code></pre>');
text = text.replace(/`(.*?)`/gim, '<code>$1</code>');
// 处理链接
text = text.replace(/\[([^\[]+)\]\(([^\)]+)\)/gim, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
// 处理图片
text = text.replace(/!\[([^\[]+)\]\(([^\)]+)\)/gim, '<img src="$2" alt="$1" style="max-width: 100%; height: auto;" />');
// 处理列表
text = text.replace(/^\s*-\s(.*$)/gim, '<li>$1</li>');
text = text.replace(/(<li>.*<\/li>)/gim, '<ul>$1</ul>');
// 处理换行
text = text.replace(/\n/gim, '<br>');
// 处理段落
text = text.replace(/<br><br>/gim, '</p><p>');
text = '<p>' + text + '</p>';
text = text.replace(/<p><(h[1-6]|ul|pre|img)/gim, '</p><$1');
text = text.replace(/(<\/(h[1-6]|ul|pre|img)>)<p>/gim, '$1</p><p>');
return text;
};
const htmlContent = parseMarkdown(content);
// 清理HTML内容:移除br标签和空p段落
const cleanHtml = htmlContent
.trim()
// 移除所有<br>标签
.replace(/<br\s*\/?>/gi, '')
// 移除空的<p>段落只包含空格或换行符
.replace(/<p[^>]*>\s*<\/p>/gi, '')
// 移除只包含空格的<p>段落
.replace(/<p[^>]*>(\s|&nbsp;)*<\/p>/gi, '')
// 移除连续的空白段落
.replace(/(<\/p>\s*<p[^>]*>)+/gi, '')
// 移除开头和结尾的空白段落
.replace(/^\s*<p[^>]*>\s*<\/p>/gi, '')
.replace(/<p[^>]*>\s*<\/p>\s*$/gi, '')
.trim();
// 检查内容是否为空或只有空白
if (!cleanHtml || cleanHtml === '<p></p>' || cleanHtml === '<p>&nbsp;</p>') {
return ''; // 如果内容为空,返回空字符串不展示
}
return `<div class="message-markdown">
<div class="markdown-content">${cleanHtml}</div>
</div>`;
} }
}; };
...@@ -475,6 +557,44 @@ const processSSEMessage = ( ...@@ -475,6 +557,44 @@ const processSSEMessage = (
// 根据是否为历史数据设置默认展开状态 // 根据是否为历史数据设置默认展开状态
const defaultThinkBoxExpanded = !isHistoryData; const defaultThinkBoxExpanded = !isHistoryData;
// 辅助函数:检查最后一个contentBlock是否是type为4的markdown块
const isLastBlockMarkdown = () => {
if (!updatedResponse || updatedResponse.contentBlocks.length === 0) {
return false;
}
const lastBlock = updatedResponse.contentBlocks[updatedResponse.contentBlocks.length - 1];
return lastBlock.content.includes('message-markdown');
};
// 辅助函数:获取最后一个markdown块的索引
const getLastMarkdownBlockIndex = () => {
if (!updatedResponse || updatedResponse.contentBlocks.length === 0) {
return -1;
}
for (let i = updatedResponse.contentBlocks.length - 1; i >= 0; i--) {
if (updatedResponse.contentBlocks[i].content.includes('message-markdown')) {
return i;
}
}
return -1;
};
// 辅助函数:合并markdown内容
const mergeMarkdownContent = (existingContent: string, newContent: string) => {
// 从现有的markdown内容中提取内部内容
const existingInnerContent = existingContent.replace(/<div class="message-markdown">\s*<div class="markdown-content">([\s\S]*?)<\/div>\s*<\/div>/, '$1');
// 从新的markdown内容中提取内部内容
const newInnerContent = newContent.replace(/<div class="message-markdown">\s*<div class="markdown-content">([\s\S]*?)<\/div>\s*<\/div>/, '$1');
// 合并内容并重新包裹
const mergedInnerContent = existingInnerContent + newInnerContent;
return `<div class="message-markdown">
<div class="markdown-content">${mergedInnerContent}</div>
</div>`;
};
switch (contentStatus) { switch (contentStatus) {
case -1: // 错误信息 case -1: // 错误信息
if (updatedResponse) { if (updatedResponse) {
...@@ -531,8 +651,33 @@ const processSSEMessage = ( ...@@ -531,8 +651,33 @@ const processSSEMessage = (
}); });
} }
} }
break; break;
case 4: // MD格式
if (updatedResponse) {
const markdownContent = contentTemplates.markdown(messageContent || '');
// 检查最后一个块是否是markdown块
if (isLastBlockMarkdown()) {
// 合并到现有的markdown块
const lastMarkdownIndex = getLastMarkdownBlockIndex();
if (lastMarkdownIndex !== -1) {
updatedResponse.contentBlocks[lastMarkdownIndex].content =
mergeMarkdownContent(
updatedResponse.contentBlocks[lastMarkdownIndex].content,
markdownContent
);
}
} else {
// 创建新的markdown块
updatedResponse.contentBlocks.push({
content: markdownContent,
hasThinkBox: false,
thinkContent: '',
thinkBoxExpanded: false,
});
}
}
break;
default: // 默认处理 default: // 默认处理
updatedResponse.contentBlocks.push({ updatedResponse.contentBlocks.push({
content: contentTemplates.text(messageContent || ''), content: contentTemplates.text(messageContent || ''),
...@@ -543,7 +688,7 @@ const processSSEMessage = ( ...@@ -543,7 +688,7 @@ const processSSEMessage = (
break; break;
} }
} }
break; break;
case 10: // 思考开始 case 10: // 思考开始
updatedIsThinking = true; updatedIsThinking = true;
if (updatedBlockIndex === -1 && updatedResponse) { if (updatedBlockIndex === -1 && updatedResponse) {
......
...@@ -319,7 +319,7 @@ p, h1, h2, h3, h4, h5, h6, ul, ol, li { ...@@ -319,7 +319,7 @@ p, h1, h2, h3, h4, h5, h6, ul, ol, li {
.think-line { .think-line {
font-size: 13px; font-size: 13px;
color: @gray-5; color: @gray-7;
font-style: italic; font-style: italic;
} }
} }
...@@ -530,7 +530,7 @@ text-overflow: ellipsis; ...@@ -530,7 +530,7 @@ text-overflow: ellipsis;
} }
} }
// 趋势箭头样式 // 趋势箭头样式
.trend-up { .trend-up {
color: @success-color; color: @success-color;
font-weight: bold; font-weight: bold;
...@@ -708,7 +708,7 @@ width: 100%; ...@@ -708,7 +708,7 @@ width: 100%;
} }
100% { 100% {
transform: translateX(200%); transform: translateX(200%);
} }
} }
// ============================================= // =============================================
...@@ -732,7 +732,7 @@ width: 100%; ...@@ -732,7 +732,7 @@ width: 100%;
} }
.table-summary { .table-summary {
font-size: 12px; font-size: 12px;
padding: 8px 10px; padding: 8px 10px;
} }
} }
...@@ -932,6 +932,193 @@ width: 100%; ...@@ -932,6 +932,193 @@ width: 100%;
} }
} }
// =============================================
// Markdown消息样式
// =============================================
:deep(.message-markdown) {
width: 100%;
max-width: 100%;
margin: 8px 0;
border-radius: 8px;
background-color: @white;
border: 1px solid @blue-light-3;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
.markdown-content {
padding: 12px 16px;
font-size: 14px;
line-height: 1.6;
color: @gray-7;
background: none;
// 标题样式
h1, h2, h3, h4, h5, h6 {
margin-bottom: 8px;
font-weight: 600;
color: @gray-7;
line-height: 1.3;
background: none;
}
h1 {
font-size: 20px;
}
h2 {
font-size: 18px;
}
h3 {
font-size: 16px;
}
// 段落样式
p {
text-align: justify;
background: none;
margin-bottom: 8px;
}
// 链接样式
a {
color: @primary-color;
text-decoration: none;
border-bottom: 1px solid transparent;
transition: all 0.2s ease;
background: none;
&:hover {
color: @primary-hover;
border-bottom-color: @primary-hover;
}
}
// 代码样式
code {
background-color: @gray-2;
padding: 2px 6px;
border-radius: 3px;
font-family: 'Courier New', monospace;
font-size: 13px;
color: @error-color;
}
pre {
background-color: @gray-1;
border: 1px solid @gray-3;
border-radius: 6px;
padding: 12px;
overflow-x: auto;
margin-bottom: 8px;
code {
background: none;
padding: 0;
color: @gray-7;
font-size: 13px;
line-height: 1.4;
}
}
// 列表样式
ul, ol {
padding-left: 24px;
margin-bottom: 8px;
}
ul {
list-style-type: disc;
}
ol {
list-style-type: decimal;
}
// 粗体和斜体
strong {
font-weight: 600;
color: @gray-7;
}
em {
font-style: italic;
color: @gray-6;
}
// 图片样式
img {
max-width: 100%;
height: auto;
border-radius: 4px;
margin-bottom: 8px;
}
// 引用块样式
blockquote {
border-left: 4px solid @primary-color;
margin-bottom: 8px;
padding: 0 16px;
font-style: italic;
color: @gray-6;
background: none;
}
// 表格样式(如果Markdown中包含表格)
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 8px;
th, td {
padding: 8px 12px;
border: 1px solid @gray-3;
text-align: left;
}
th {
background-color: @blue-light-2;
font-weight: 600;
}
tr:nth-child(even) {
background-color: @gray-1;
}
}
}
}
// 响应式调整
@media (max-width: 768px) {
:deep(.message-markdown) {
.markdown-content {
font-size: 13px;
padding: 10px 12px;
h1 {
font-size: 18px;
}
h2 {
font-size: 16px;
}
h3 {
font-size: 15px;
}
pre {
padding: 8px;
code {
font-size: 12px;
}
}
}
}
}
// 语音识别组件样式调整 // 语音识别组件样式调整
.chat-input { .chat-input {
display: flex; display: flex;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment