This commit is contained in:
HuangHai
2026-01-21 08:51:44 +08:00
parent 7206bd554c
commit 2fda6f0dcf
6 changed files with 156 additions and 61 deletions

View File

@@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>分时电价分析 - 驿来特AI智能大脑</title>
<link rel="stylesheet" href="css/dashboard.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.5.1/github-markdown-dark.min.css">
</head>
<body>
<div id="app">
@@ -82,7 +83,7 @@
<span>🤖 AI 调价策略建议</span>
</div>
<div class="ai-box" ref="aiBoxRef">
<div v-if="!aiText && !aiLoading" style="text-align: center; color: #64748b; margin-top: 40px;">
<div v-if="!aiText" style="text-align: center; color: #64748b; margin-top: 40px;">
<span class="loading-dots">AI 正在深度分析中</span>
</div>
<div v-else class="markdown-body" v-html="renderedAiText"></div>
@@ -95,9 +96,9 @@
<script src="js/axios.min.js"></script>
<script src="js/echarts.min.js"></script>
<!-- Markdown & LaTeX Support -->
<link rel="stylesheet" href="https://gcore.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<script src="https://gcore.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script src="https://gcore.jsdelivr.net/npm/marked@12.0.0/lib/marked.umd.js"></script>
<link rel="stylesheet" href="https://unpkg.com/katex@0.16.9/dist/katex.min.css">
<script src="https://unpkg.com/katex@0.16.9/dist/katex.min.js"></script>
<script src="https://unpkg.com/marked@12.0.0/marked.min.js"></script>
<script src="js/dashboard.js"></script>
</body>
</html>

View File

@@ -283,11 +283,20 @@ createApp({
const reader = response.body.getReader();
const decoder = new TextDecoder();
let fullText = "";
while (true) {
const {done, value} = await reader.read();
if (done) break;
const chunk = decoder.decode(value, {stream: true});
aiText.value += chunk;
fullText += chunk;
if (fullText.includes("---CLEAR_PREVIOUS_HINTS---")) {
const parts = fullText.split("---CLEAR_PREVIOUS_HINTS---");
aiText.value = parts[parts.length - 1];
} else {
aiText.value = fullText;
}
await Vue.nextTick();
if (aiBoxRef.value) {
aiBoxRef.value.scrollTop = aiBoxRef.value.scrollHeight;
@@ -325,7 +334,7 @@ createApp({
});
}
// 降级用的简易 Markdown 解析器
// 降级用的简易 Markdown 解析器 (增强版)
const simpleMarkdown = (text) => {
if (!text) return '';
let lines = text.split('\n');
@@ -334,6 +343,7 @@ createApp({
const parseInline = (str) => {
return str
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.*?)\*/g, '<em>$1</em>')
.replace(/`(.*?)`/g, '<code style="background:rgba(148, 163, 184, 0.1); padding:2px 4px; border-radius:4px;">$1</code>');
};
for (let line of lines) {
@@ -343,13 +353,28 @@ createApp({
html += '<br>';
continue;
}
if (trimmed.startsWith('### ')) {
if (trimmed.startsWith('# ')) {
if (inList) { html += '</ul>'; inList = false; }
html += `<h1>${parseInline(trimmed.substring(2))}</h1>`;
} else if (trimmed.startsWith('## ')) {
if (inList) { html += '</ul>'; inList = false; }
html += `<h2>${parseInline(trimmed.substring(3))}</h2>`;
} else if (trimmed.startsWith('### ')) {
if (inList) { html += '</ul>'; inList = false; }
html += `<h3>${parseInline(trimmed.substring(4))}</h3>`;
} else if (trimmed.startsWith('- ') || /^\d+\./.test(trimmed)) {
if (!inList) { html += '<ul>'; inList = true; }
let content = trimmed.replace(/^(- |\d+\. )/, '');
html += `<li>${parseInline(content)}</li>`;
} else if (trimmed.startsWith('|') && trimmed.endsWith('|')) {
// 简单的表格识别 (仅作示意,复杂表格仍需 marked)
if (inList) { html += '</ul>'; inList = false; }
let cells = trimmed.split('|').filter(c => c.trim() || c === '');
html += '<div style="display:flex; border-bottom:1px solid #334155; padding:4px;">';
cells.forEach(c => {
html += `<div style="flex:1; padding:4px;">${parseInline(c.trim())}</div>`;
});
html += '</div>';
} else {
if (inList) { html += '</ul>'; inList = false; }
html += `<p>${parseInline(trimmed)}</p>`;
@@ -363,44 +388,45 @@ createApp({
const renderMarkdownAndLatex = (text) => {
if (!text) return '';
// 如果包含提示信息且还没被清除,先简单处理显示
if (text.includes('正在收集各供应商价格数据') || text.includes('正在进行 AI 深度分析')) {
return `<div style="color: #64748b; font-style: italic;">${text.replace(/\n/g, '<br>')}</div>`;
}
try {
// 1. 处理 LaTeX (简单替换,先处理 $$ 再处理 $)
let processedText = text;
// 处理块级 LaTeX: $$ ... $$
processedText = processedText.replace(/\$\$\s*([\s\S]*?)\s*\$\$/g, (match, formula) => {
try {
if (typeof katex !== 'undefined') {
// 1. 处理 LaTeX (如果 katex 加载成功)
if (typeof katex !== 'undefined') {
// 处理块级 LaTeX: $$ ... $$
processedText = processedText.replace(/\$\$\s*([\s\S]*?)\s*\$\$/g, (match, formula) => {
try {
return '<div class="katex-block">' + katex.renderToString(formula, { displayMode: true, throwOnError: false }) + '</div>';
}
return match;
} catch (e) {
return match;
}
});
// 处理行内 LaTeX: $ ... $
processedText = processedText.replace(/\$([^\$\n]+?)\$/g, (match, formula) => {
try {
if (typeof katex !== 'undefined') {
} catch (e) { return match; }
});
// 处理行内 LaTeX: $ ... $
processedText = processedText.replace(/\$([^\$\n]+?)\$/g, (match, formula) => {
try {
return katex.renderToString(formula, { displayMode: false, throwOnError: false });
}
return match;
} catch (e) {
return match;
}
});
// 2. 使用 marked 解析 Markdown
if (typeof marked !== 'undefined') {
return marked.parse(processedText);
} else {
// 降级使用之前的 simpleMarkdown
return simpleMarkdown(processedText);
} catch (e) { return match; }
});
}
// 2. 使用 marked 解析 Markdown (优先使用)
if (typeof marked !== 'undefined') {
// 兼容不同版本的 marked (UMD 版本中 marked 可能是函数或对象)
const parseFn = (typeof marked.parse === 'function') ? marked.parse.bind(marked) : (typeof marked === 'function' ? marked : null);
if (parseFn) {
return parseFn(processedText);
}
}
// 3. 降级使用改进后的 simpleMarkdown
return simpleMarkdown(processedText);
} catch (e) {
console.error('Markdown/LaTeX rendering error:', e);
return text;
console.error('Markdown rendering error:', e);
return simpleMarkdown(text);
}
};