chore: 清理非必要的测试文件和文档
This commit is contained in:
@@ -1,187 +0,0 @@
|
||||
# AI 评论审核流程说明
|
||||
|
||||
## 流程概述
|
||||
|
||||
本文档通过 Mermaid 流程图详细展示用户提交评论后,系统进行 AI 审核的全流程,涵盖预处理、规则判断、AI 检测、结果处理、数据学习等核心环节。
|
||||
|
||||
## 流程图
|
||||
|
||||
::: mermaid
|
||||
flowchart TD
|
||||
Start([用户提交评论]) --> PreProcess[预处理评论<br/>钩子:preprocess_comment/post_comment_preprocessing]
|
||||
PreProcess --> CheckEnabled{启用 AI 检测?}
|
||||
CheckEnabled -->|否| SaveComment[保存评论]
|
||||
CheckEnabled -->|是| CheckMode{检测模式?}
|
||||
CheckMode -->|manual(关闭实时检测)| SaveComment
|
||||
CheckMode -->|keyword/sample/all| CheckLoggedIn{已登录用户?}
|
||||
CheckLoggedIn -->|是且跳过登录用户| SaveComment
|
||||
CheckLoggedIn -->|否或不跳过| CheckWhitelist{在白名单?}
|
||||
CheckWhitelist -->|是| SaveComment
|
||||
CheckWhitelist -->|否| CheckKeyword[检查关键字触发]
|
||||
CheckKeyword --> DecideCheck{决定是否检测}
|
||||
DecideCheck -->|keyword模式且触发关键字| NeedCheck[需要检测]
|
||||
DecideCheck -->|sample模式且触发关键字| NeedCheck
|
||||
DecideCheck -->|sample模式且随机抽中| NeedCheck
|
||||
DecideCheck -->|all模式| NeedCheck
|
||||
DecideCheck -->|其他情况| SaveComment
|
||||
NeedCheck --> SetPending[强制设置 comment_approved=0<br/>待审核状态]
|
||||
SetPending --> SetFlag[设置 _argon_needs_spam_check=true]
|
||||
SetFlag --> SaveKeywords[保存触发的关键字]
|
||||
SaveKeywords --> SaveComment
|
||||
SaveComment --> CommentPost[评论入库后处理<br/>钩子:comment_post/post_comment_updatemetas]
|
||||
CommentPost --> SaveMeta[保存评论元数据]
|
||||
SaveMeta --> CheckNeedFlag{有 _argon_needs_spam_check 标记?}
|
||||
CheckNeedFlag -->|否| ShowPending[显示审核中状态]
|
||||
CheckNeedFlag -->|是| SaveCheckMeta[保存检测标记到数据库]
|
||||
SaveCheckMeta --> AutoDetect[触发自动检测<br/>钩子:comment_post/argon_auto_detect_spam_on_comment]
|
||||
AutoDetect --> CheckSaved{有检测标记?}
|
||||
CheckSaved -->|否| End1([结束])
|
||||
CheckSaved -->|是| CheckDetected{已检测过?}
|
||||
CheckDetected -->|是| End2([结束])
|
||||
CheckDetected -->|否| CheckReason{检测原因?}
|
||||
CheckReason -->|关键字触发| SyncDetect[立即同步检测]
|
||||
CheckReason -->|其他| AsyncDetect[异步检测(延迟1秒)]
|
||||
SyncDetect --> DetectHandler[检测处理函数<br/>argon_async_spam_detection_handler]
|
||||
AsyncDetect --> DetectHandler
|
||||
DetectHandler --> CallAI[调用 AI API<br/>argon_detect_spam_comment]
|
||||
CallAI --> GetPrompt[根据 Prompt 模式获取提示词]
|
||||
CallAI --> BuildContext[构建评论上下文(用户名+内容)]
|
||||
BuildContext --> SendAPI[发送到 AI API]
|
||||
SendAPI --> ParseResult[解析 AI 返回结果]
|
||||
ParseResult --> SaveTime[记录检测时间]
|
||||
SaveTime --> GenCode[生成识别码]
|
||||
GenCode --> UpdateStats[更新用户统计]
|
||||
UpdateStats --> CheckResult{检测结果?}
|
||||
CheckResult -->|内容违规(高置信度)| CheckAction1{自动处理方式?}
|
||||
CheckResult -->|内容违规(低置信度)| MarkLowConf[标记为疑似垃圾<br/>等待人工审核]
|
||||
CheckResult -->|内容正常/用户名违规/无邮箱| TrashNoEmail[移入回收站]
|
||||
CheckResult -->|内容正常/用户名违规/有邮箱| ChangeUsername[修改用户名]
|
||||
CheckResult -->|都正常| MarkNormal[标记为正常评论]
|
||||
CheckAction1 -->|trash| TrashComment[移入回收站]
|
||||
CheckAction1 -->|hold| HoldComment[标记为待审核]
|
||||
CheckAction1 -->|mark| MarkOnly[仅标记不处理]
|
||||
TrashComment --> SendSpamEmail[发送垃圾评论通知]
|
||||
HoldComment --> SendSpamEmail
|
||||
MarkOnly --> SaveResult1[保存检测结果]
|
||||
SendSpamEmail --> SaveResult1
|
||||
MarkLowConf --> SaveResult2[保存检测结果]
|
||||
TrashNoEmail --> SaveResult3[保存检测结果]
|
||||
ChangeUsername --> GenNewName[生成新用户名]
|
||||
GenNewName --> UpdateDB[更新数据库]
|
||||
UpdateDB --> SendChangeEmail[发送用户名变更通知]
|
||||
SendChangeEmail --> SaveResult4[保存检测结果]
|
||||
MarkNormal --> SaveResult5[保存检测结果]
|
||||
SaveResult1 --> AILearn{启用 AI 学习?}
|
||||
SaveResult2 --> AILearn
|
||||
SaveResult3 --> AILearn
|
||||
SaveResult4 --> AILearn
|
||||
SaveResult5 --> AILearn
|
||||
AILearn -->|是| ExtractKeywords[提取关键词]
|
||||
AILearn -->|否| DetectEnd([检测完成])
|
||||
ExtractKeywords --> UpdateKeywords[更新学习关键词库]
|
||||
UpdateKeywords --> DetectEnd
|
||||
ShowPending --> UserView([用户看到:审核中状态])
|
||||
DetectEnd --> AdminReview([管理员审核])
|
||||
|
||||
style Start fill:#e1f5e1,stroke:#2e7d32,stroke-width:2px
|
||||
style End1 fill:#ffe1e1,stroke:#c62828,stroke-width:2px
|
||||
style End2 fill:#ffe1e1,stroke:#c62828,stroke-width:2px
|
||||
style DetectEnd fill:#e1f5e1,stroke:#2e7d32,stroke-width:2px
|
||||
style UserView fill:#fff4e1,stroke:#ff8f00,stroke-width:2px
|
||||
style AdminReview fill:#e1e8ff,stroke:#1565c0,stroke-width:2px
|
||||
style NeedCheck fill:#ffcccc,stroke:#c62828,stroke-width:1px
|
||||
style SetPending fill:#ffcccc,stroke:#c62828,stroke-width:1px
|
||||
style TrashComment fill:#ff6b6b,stroke:#c62828,stroke-width:1px
|
||||
style TrashNoEmail fill:#ff6b6b,stroke:#c62828,stroke-width:1px
|
||||
style HoldComment fill:#ffa500,stroke:#ff8f00,stroke-width:1px
|
||||
style MarkLowConf fill:#ffa500,stroke:#ff8f00,stroke-width:1px
|
||||
style ChangeUsername fill:#4ecdc4,stroke:#00897b,stroke-width:1px
|
||||
style MarkNormal fill:#95e1d3,stroke:#2e7d32,stroke-width:1px
|
||||
:::
|
||||
|
||||
## 流程关键节点说明
|
||||
|
||||
### 1. 预处理阶段
|
||||
|
||||
- 触发 `preprocess_comment`/`post_comment_preprocessing` 钩子,完成评论基础清洗。
|
||||
- 优先判断是否启用 AI 检测,未启用则直接保存评论。
|
||||
|
||||
### 2. 检测规则判断
|
||||
|
||||
- 检测模式分 4 类:manual(关闭)、keyword(关键字)、sample(抽样)、all(全量)。
|
||||
- 白名单用户、已登录且配置跳过的用户,直接跳过检测。
|
||||
- keyword/sample 模式下,仅关键字触发/随机抽中时才启动检测。
|
||||
|
||||
### 3. AI 检测执行
|
||||
|
||||
- 关键字触发:立即同步检测;其他情况:延迟 1 秒异步检测。
|
||||
- 调用 `argon_detect_spam_comment` 接口,拼接用户名+评论内容作为上下文发送 AI。
|
||||
|
||||
### 4. 结果处理逻辑
|
||||
|
||||
| 检测结果 | 处理方式 |
|
||||
|-------------------------|-----------------------------------|
|
||||
| 内容违规(高置信度)| 按配置自动处理(移入回收站/待审核/仅标记) |
|
||||
| 内容违规(低置信度)| 标记疑似垃圾,等待人工审核 |
|
||||
| 用户名违规(无邮箱)| 直接移入回收站 |
|
||||
| 用户名违规(有邮箱)| 自动生成新用户名并通知用户 |
|
||||
| 内容+用户名均正常 | 标记为正常评论 |
|
||||
|
||||
### 5. 后续环节
|
||||
|
||||
- 所有检测结果均保存至数据库,支持 AI 学习功能(提取关键词更新词库)。
|
||||
- 用户侧仅展示"审核中"状态,最终结果需管理员复核。
|
||||
|
||||
## 技术说明
|
||||
|
||||
### 使用的钩子
|
||||
|
||||
- `preprocess_comment` - 评论预处理
|
||||
- `post_comment_preprocessing` - 评论预处理(备用)
|
||||
- `comment_post` - 评论入库后
|
||||
- `post_comment_updatemetas` - 更新评论元数据
|
||||
- `argon_auto_detect_spam_on_comment` - 自动检测触发
|
||||
|
||||
### 关键函数
|
||||
|
||||
- `argon_detect_spam_comment()` - AI 检测主函数
|
||||
- `argon_async_spam_detection_handler()` - 异步检测处理
|
||||
- `argon_get_spam_detection_prompt()` - 获取检测 Prompt
|
||||
- `argon_build_comment_context()` - 构建评论上下文
|
||||
|
||||
### 数据库字段
|
||||
|
||||
- `comment_approved` - 评论审核状态(0=待审核,1=已通过)
|
||||
- `_argon_needs_spam_check` - 是否需要 AI 检测标记
|
||||
- `_argon_spam_detection_result` - AI 检测结果
|
||||
- `_argon_spam_detection_time` - 检测时间戳
|
||||
- `_argon_spam_keywords` - 触发的关键字
|
||||
|
||||
## 配置建议
|
||||
|
||||
### 小型博客(评论量 < 100/天)
|
||||
|
||||
- 检测模式:keyword(关键字触发)
|
||||
- Prompt 模式:标准模式
|
||||
- 自动处理阈值:置信度 > 0.9
|
||||
|
||||
### 中型博客(评论量 100-500/天)
|
||||
|
||||
- 检测模式:sample(智能抽样)
|
||||
- Prompt 模式:标准模式
|
||||
- 自动处理阈值:置信度 > 0.85
|
||||
|
||||
### 大型博客(评论量 > 500/天)
|
||||
|
||||
- 检测模式:sample(智能抽样 30-40%)
|
||||
- Prompt 模式:极简模式
|
||||
- 自动处理阈值:置信度 > 0.8
|
||||
- 定期批量扫描待审核评论
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **API 成本控制**:合理设置检测模式和抽样比例
|
||||
2. **误判处理**:始终保留人工审核入口
|
||||
3. **隐私保护**:不要将敏感信息发送给 AI
|
||||
4. **性能优化**:关键字触发使用同步检测,其他使用异步
|
||||
5. **定期优化**:根据误判率调整 Prompt 和阈值
|
||||
@@ -1,297 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Mermaid 代码块魔改测试</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.test-case {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
.test-case h3 {
|
||||
margin-top: 0;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #007bff;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.test-case .description {
|
||||
color: #666;
|
||||
margin-bottom: 15px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.mermaid-from-codeblock {
|
||||
background: #f8f9fa;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
pre {
|
||||
background: #f8f9fa;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #dee2e6;
|
||||
overflow-x: auto;
|
||||
}
|
||||
code {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
}
|
||||
</style>
|
||||
<!-- Mermaid 库 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Mermaid 代码块魔改测试</h1>
|
||||
<p>本页面用于测试标准 Markdown 代码块 (<code>```mermaid</code>) 的 Mermaid 图表渲染功能。</p>
|
||||
|
||||
<!-- 测试用例 1:标准格式 -->
|
||||
<div class="test-case">
|
||||
<h3>测试用例 1:标准 Markdown 格式</h3>
|
||||
<div class="description">
|
||||
格式:<code><pre><code class="language-mermaid"></code>
|
||||
</div>
|
||||
<pre><code class="language-mermaid">
|
||||
flowchart TD
|
||||
A[开始] --> B{判断}
|
||||
B -->|是| C[执行操作]
|
||||
B -->|否| D[跳过]
|
||||
C --> E[结束]
|
||||
D --> E
|
||||
</code></pre>
|
||||
</div>
|
||||
|
||||
<!-- 测试用例 2:多个代码块 -->
|
||||
<div class="test-case">
|
||||
<h3>测试用例 2:多个代码块(批量处理)</h3>
|
||||
<div class="description">
|
||||
测试同一页面中多个 Mermaid 代码块的处理
|
||||
</div>
|
||||
<pre><code class="language-mermaid">
|
||||
graph LR
|
||||
A[客户端] --> B[服务器]
|
||||
B --> C[数据库]
|
||||
C --> B
|
||||
B --> A
|
||||
</code></pre>
|
||||
<br>
|
||||
<pre><code class="language-mermaid">
|
||||
sequenceDiagram
|
||||
participant 用户
|
||||
participant 系统
|
||||
用户->>系统: 发送请求
|
||||
系统->>用户: 返回响应
|
||||
</code></pre>
|
||||
</div>
|
||||
|
||||
<!-- 测试用例 3:特殊字符 -->
|
||||
<div class="test-case">
|
||||
<h3>测试用例 3:特殊字符保留</h3>
|
||||
<div class="description">
|
||||
测试箭头符号 <code>--></code>、双横线 <code>--</code>、等号 <code>==</code> 等特殊字符是否正确保留
|
||||
</div>
|
||||
<pre><code class="language-mermaid">
|
||||
flowchart TD
|
||||
A --> B
|
||||
B -- 文本 --> C
|
||||
C ==> D
|
||||
D -.-> E
|
||||
E ~~~ F
|
||||
</code></pre>
|
||||
</div>
|
||||
|
||||
<!-- 测试用例 4:空代码块 -->
|
||||
<div class="test-case">
|
||||
<h3>测试用例 4:空代码块(边界情况)</h3>
|
||||
<div class="description">
|
||||
测试空代码块是否会导致错误
|
||||
</div>
|
||||
<pre><code class="language-mermaid"></code></pre>
|
||||
<p style="color: #666; font-size: 14px;">空代码块应该被跳过,不会创建容器</p>
|
||||
</div>
|
||||
|
||||
<!-- 测试用例 5:多行代码块 -->
|
||||
<div class="test-case">
|
||||
<h3>测试用例 5:多行代码块(换行符保留)</h3>
|
||||
<div class="description">
|
||||
测试多行代码块中的换行符是否正确保留
|
||||
</div>
|
||||
<pre><code class="language-mermaid">
|
||||
graph TB
|
||||
subgraph 子图1
|
||||
A1[节点A1]
|
||||
A2[节点A2]
|
||||
end
|
||||
subgraph 子图2
|
||||
B1[节点B1]
|
||||
B2[节点B2]
|
||||
end
|
||||
A1 --> B1
|
||||
A2 --> B2
|
||||
</code></pre>
|
||||
</div>
|
||||
|
||||
<!-- 测试用例 6:简化格式 -->
|
||||
<div class="test-case">
|
||||
<h3>测试用例 6:简化格式</h3>
|
||||
<div class="description">
|
||||
格式:<code><pre><code class="mermaid"></code>
|
||||
</div>
|
||||
<pre><code class="mermaid">
|
||||
pie title 数据分布
|
||||
"类别A" : 45
|
||||
"类别B" : 30
|
||||
"类别C" : 25
|
||||
</code></pre>
|
||||
</div>
|
||||
|
||||
<!-- 测试用例 7:无 pre 包裹 -->
|
||||
<div class="test-case">
|
||||
<h3>测试用例 7:无 pre 包裹</h3>
|
||||
<div class="description">
|
||||
格式:<code><code class="language-mermaid"></code>(无 pre 标签)
|
||||
</div>
|
||||
<code class="language-mermaid">
|
||||
flowchart LR
|
||||
Start --> Stop
|
||||
</code>
|
||||
</div>
|
||||
|
||||
<!-- 测试用例 8:自定义属性格式 -->
|
||||
<div class="test-case">
|
||||
<h3>测试用例 8:自定义属性格式</h3>
|
||||
<div class="description">
|
||||
格式:<code><pre data-lang="mermaid"></code>
|
||||
</div>
|
||||
<pre data-lang="mermaid">
|
||||
stateDiagram-v2
|
||||
[*] --> 状态1
|
||||
状态1 --> 状态2
|
||||
状态2 --> [*]
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<!-- 测试用例 9:复杂图表 -->
|
||||
<div class="test-case">
|
||||
<h3>测试用例 9:复杂图表</h3>
|
||||
<div class="description">
|
||||
测试复杂的 Mermaid 图表渲染
|
||||
</div>
|
||||
<pre><code class="language-mermaid">
|
||||
classDiagram
|
||||
Animal <|-- Duck
|
||||
Animal <|-- Fish
|
||||
Animal <|-- Zebra
|
||||
Animal : +int age
|
||||
Animal : +String gender
|
||||
Animal: +isMammal()
|
||||
Animal: +mate()
|
||||
class Duck{
|
||||
+String beakColor
|
||||
+swim()
|
||||
+quack()
|
||||
}
|
||||
class Fish{
|
||||
-int sizeInFeet
|
||||
-canEat()
|
||||
}
|
||||
class Zebra{
|
||||
+bool is_wild
|
||||
+run()
|
||||
}
|
||||
</code></pre>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 初始化 Mermaid
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
theme: 'default',
|
||||
securityLevel: 'loose'
|
||||
});
|
||||
|
||||
// 模拟主题的 convertMermaidCodeblocks 函数
|
||||
function convertMermaidCodeblocks(){
|
||||
const selectors = [
|
||||
'pre > code.language-mermaid',
|
||||
'pre > code.mermaid',
|
||||
'code.language-mermaid',
|
||||
'pre[data-lang="mermaid"]'
|
||||
];
|
||||
|
||||
let processedCount = 0;
|
||||
|
||||
selectors.forEach(selector => {
|
||||
document.querySelectorAll(selector).forEach(element => {
|
||||
if (element.dataset.mermaidProcessed) {
|
||||
return;
|
||||
}
|
||||
|
||||
let code = element.textContent.trim();
|
||||
if (!code) {
|
||||
return;
|
||||
}
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.className = 'mermaid-from-codeblock';
|
||||
container.textContent = code;
|
||||
container.dataset.processed = 'true';
|
||||
|
||||
const targetElement = element.closest('pre') || element;
|
||||
if (targetElement.parentNode) {
|
||||
targetElement.parentNode.replaceChild(container, targetElement);
|
||||
processedCount++;
|
||||
}
|
||||
|
||||
element.dataset.mermaidProcessed = 'true';
|
||||
});
|
||||
});
|
||||
|
||||
console.log('[Mermaid] 转换了 ' + processedCount + ' 个代码块');
|
||||
}
|
||||
|
||||
// 渲染 Mermaid 图表
|
||||
function renderMermaidCharts(){
|
||||
const blocks = document.querySelectorAll('.mermaid-from-codeblock');
|
||||
console.log('[Mermaid] 找到 ' + blocks.length + ' 个待渲染的图表');
|
||||
|
||||
blocks.forEach((block, index) => {
|
||||
const id = 'mermaid-' + Date.now() + '-' + index;
|
||||
const code = block.textContent;
|
||||
|
||||
try {
|
||||
// Mermaid 10.x 使用 render 方法,返回 Promise
|
||||
mermaid.render(id, code).then(function(result) {
|
||||
block.innerHTML = result.svg;
|
||||
console.log('[Mermaid] 成功渲染图表 ' + (index + 1));
|
||||
}).catch(function(error) {
|
||||
console.error('[Mermaid] 渲染失败:', error);
|
||||
block.innerHTML = '<div style="color: red; padding: 10px; border: 1px solid red; border-radius: 4px;">渲染失败: ' + error.message + '</div>';
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[Mermaid] 渲染异常:', error);
|
||||
block.innerHTML = '<div style="color: red; padding: 10px; border: 1px solid red; border-radius: 4px;">渲染异常: ' + error.message + '</div>';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 页面加载完成后执行
|
||||
document.addEventListener('DOMContentLoaded', function(){
|
||||
console.log('[测试] 开始执行代码块转换');
|
||||
convertMermaidCodeblocks();
|
||||
|
||||
console.log('[测试] 开始渲染 Mermaid 图表');
|
||||
setTimeout(renderMermaidCharts, 100);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,374 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Mermaid 库加载失败降级处理测试</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.test-section {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #333;
|
||||
border-bottom: 2px solid #5e72e4;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #5e72e4;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.mermaid {
|
||||
background: #f8f9fa;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.mermaid-error-container {
|
||||
background: #fff5f5;
|
||||
border: 1px solid #fc8181;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.mermaid-error-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.mermaid-error-icon {
|
||||
font-size: 24px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.mermaid-error-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #c53030;
|
||||
}
|
||||
|
||||
.mermaid-error-body {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.mermaid-error-type {
|
||||
font-weight: bold;
|
||||
color: #742a2a;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.mermaid-error-message {
|
||||
color: #742a2a;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.mermaid-error-code {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.mermaid-error-code summary {
|
||||
cursor: pointer;
|
||||
color: #5e72e4;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.mermaid-error-code pre {
|
||||
background: #2d3748;
|
||||
color: #e2e8f0;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
overflow-x: auto;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.test-log {
|
||||
background: #2d3748;
|
||||
color: #e2e8f0;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 14px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.test-log div {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.log-info {
|
||||
color: #63b3ed;
|
||||
}
|
||||
|
||||
.log-warn {
|
||||
color: #f6ad55;
|
||||
}
|
||||
|
||||
.log-error {
|
||||
color: #fc8181;
|
||||
}
|
||||
|
||||
.log-success {
|
||||
color: #68d391;
|
||||
}
|
||||
|
||||
.test-controls {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.test-controls button {
|
||||
background: #5e72e4;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.test-controls button:hover {
|
||||
background: #4c63d2;
|
||||
}
|
||||
|
||||
.test-controls button:disabled {
|
||||
background: #cbd5e0;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🧪 Mermaid 库加载失败降级处理测试</h1>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>测试说明</h2>
|
||||
<p>本测试页面用于验证 Mermaid 库加载失败时的降级处理机制。</p>
|
||||
<ul>
|
||||
<li><strong>测试 1</strong>: 主 CDN 加载失败,自动尝试备用 CDN</li>
|
||||
<li><strong>测试 2</strong>: 所有 CDN 都失败,显示友好的错误提示</li>
|
||||
<li><strong>测试 3</strong>: 备用 CDN 加载成功,正常渲染图表</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>测试控制</h2>
|
||||
<div class="test-controls">
|
||||
<button onclick="testFailedMainCDN()">测试 1: 主 CDN 失败</button>
|
||||
<button onclick="testAllCDNsFailed()">测试 2: 所有 CDN 失败</button>
|
||||
<button onclick="testSuccessfulFallback()">测试 3: 备用 CDN 成功</button>
|
||||
<button onclick="clearLog()">清空日志</button>
|
||||
</div>
|
||||
<div id="testLog" class="test-log"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>测试图表</h2>
|
||||
<div class="mermaid">
|
||||
flowchart TD
|
||||
A[开始] --> B{主 CDN 加载}
|
||||
B -->|成功| C[渲染图表]
|
||||
B -->|失败| D[尝试备用 CDN 1]
|
||||
D -->|成功| C
|
||||
D -->|失败| E[尝试备用 CDN 2]
|
||||
E -->|成功| C
|
||||
E -->|失败| F[显示错误提示]
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 模拟降级处理脚本
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// 备用 CDN URL 列表
|
||||
const fallbackUrls = [
|
||||
'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js',
|
||||
'https://unpkg.com/mermaid@10/dist/mermaid.min.js',
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.0.0/mermaid.min.js'
|
||||
];
|
||||
let loadAttempted = false;
|
||||
|
||||
// 日志函数
|
||||
function log(message, type = 'info') {
|
||||
const logDiv = document.getElementById('testLog');
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
const logEntry = document.createElement('div');
|
||||
logEntry.className = `log-${type}`;
|
||||
logEntry.textContent = `[${timestamp}] ${message}`;
|
||||
logDiv.appendChild(logEntry);
|
||||
logDiv.scrollTop = logDiv.scrollHeight;
|
||||
|
||||
// 同时输出到控制台
|
||||
console.log(`[Argon Mermaid] ${message}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试从备用 CDN 加载 Mermaid 库
|
||||
*/
|
||||
window.argonMermaidLoadFallback = function() {
|
||||
// 避免重复调用
|
||||
if (loadAttempted) {
|
||||
return;
|
||||
}
|
||||
loadAttempted = true;
|
||||
|
||||
log('主 CDN 加载失败,尝试备用 CDN', 'warn');
|
||||
|
||||
// 尝试加载备用 CDN
|
||||
loadMermaidWithFallback(fallbackUrls, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* 递归加载备用 CDN
|
||||
* @param {Array} urls - CDN URL 列表
|
||||
* @param {number} index - 当前尝试的索引
|
||||
*/
|
||||
function loadMermaidWithFallback(urls, index) {
|
||||
// 如果所有 CDN 都失败了
|
||||
if (index >= urls.length) {
|
||||
log('所有 CDN 加载失败', 'error');
|
||||
showGlobalError();
|
||||
return;
|
||||
}
|
||||
|
||||
const url = urls[index];
|
||||
log(`尝试从备用 CDN 加载: ${url}`, 'info');
|
||||
|
||||
// 创建 script 标签
|
||||
const script = document.createElement('script');
|
||||
script.src = url;
|
||||
script.async = true;
|
||||
|
||||
// 加载失败,尝试下一个 CDN
|
||||
script.onerror = function() {
|
||||
log(`CDN ${url} 加载失败`, 'warn');
|
||||
loadMermaidWithFallback(urls, index + 1);
|
||||
};
|
||||
|
||||
// 加载成功,初始化 Mermaid
|
||||
script.onload = function() {
|
||||
log(`成功从备用 CDN 加载: ${url}`, 'success');
|
||||
|
||||
// 初始化 Mermaid
|
||||
if (typeof window.mermaid !== 'undefined') {
|
||||
window.mermaid.initialize({
|
||||
startOnLoad: true,
|
||||
theme: 'default'
|
||||
});
|
||||
log('Mermaid 初始化成功', 'success');
|
||||
}
|
||||
};
|
||||
|
||||
// 添加到页面
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示全局错误提示
|
||||
*/
|
||||
function showGlobalError() {
|
||||
// 查找所有 Mermaid 代码块
|
||||
const selectors = [
|
||||
'div.mermaid',
|
||||
'pre code.language-mermaid',
|
||||
'pre[data-lang="mermaid"]',
|
||||
'code.mermaid'
|
||||
];
|
||||
|
||||
let blocks = [];
|
||||
selectors.forEach(function(selector) {
|
||||
const elements = document.querySelectorAll(selector);
|
||||
elements.forEach(function(element) {
|
||||
if (!blocks.includes(element)) {
|
||||
blocks.push(element);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
log(`找到 ${blocks.length} 个 Mermaid 代码块,显示错误提示`, 'info');
|
||||
|
||||
// 在每个代码块位置显示错误提示
|
||||
blocks.forEach(function(block) {
|
||||
const errorContainer = document.createElement('div');
|
||||
errorContainer.className = 'mermaid-error-container';
|
||||
errorContainer.innerHTML = `
|
||||
<div class="mermaid-error-header">
|
||||
<span class="mermaid-error-icon">⚠️</span>
|
||||
<span class="mermaid-error-title">Mermaid 库加载失败</span>
|
||||
</div>
|
||||
<div class="mermaid-error-body">
|
||||
<p class="mermaid-error-type">错误类型: 网络错误</p>
|
||||
<p class="mermaid-error-message">无法从任何 CDN 加载 Mermaid 库,请检查网络连接或联系管理员。</p>
|
||||
</div>
|
||||
<details class="mermaid-error-code">
|
||||
<summary>查看原始代码</summary>
|
||||
<pre><code class="language-mermaid">${escapeHtml(block.textContent)}</code></pre>
|
||||
</details>
|
||||
`;
|
||||
|
||||
block.parentNode.replaceChild(errorContainer, block);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML 转义
|
||||
* @param {string} text - 要转义的文本
|
||||
* @returns {string} 转义后的文本
|
||||
*/
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
// 测试函数
|
||||
window.testFailedMainCDN = function() {
|
||||
log('=== 开始测试 1: 主 CDN 失败 ===', 'info');
|
||||
loadAttempted = false;
|
||||
argonMermaidLoadFallback();
|
||||
};
|
||||
|
||||
window.testAllCDNsFailed = function() {
|
||||
log('=== 开始测试 2: 所有 CDN 失败 ===', 'info');
|
||||
loadAttempted = false;
|
||||
// 使用无效的 URL 列表
|
||||
loadMermaidWithFallback(['https://invalid-cdn-1.com/mermaid.js', 'https://invalid-cdn-2.com/mermaid.js'], 0);
|
||||
};
|
||||
|
||||
window.testSuccessfulFallback = function() {
|
||||
log('=== 开始测试 3: 备用 CDN 成功 ===', 'info');
|
||||
loadAttempted = false;
|
||||
// 直接加载有效的 CDN
|
||||
loadMermaidWithFallback(fallbackUrls, 0);
|
||||
};
|
||||
|
||||
window.clearLog = function() {
|
||||
document.getElementById('testLog').innerHTML = '';
|
||||
log('日志已清空', 'info');
|
||||
};
|
||||
|
||||
// 页面加载完成后的初始化
|
||||
log('测试页面加载完成', 'success');
|
||||
log('点击上方按钮开始测试', 'info');
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,276 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Mermaid 修复测试</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.test-section {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
.test-section h2 {
|
||||
color: #5e72e4;
|
||||
border-bottom: 2px solid #5e72e4;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.test-case {
|
||||
margin: 15px 0;
|
||||
padding: 15px;
|
||||
background: #f8f9fa;
|
||||
border-left: 4px solid #5e72e4;
|
||||
}
|
||||
.test-case h3 {
|
||||
margin-top: 0;
|
||||
color: #333;
|
||||
}
|
||||
.expected {
|
||||
color: #28a745;
|
||||
font-weight: bold;
|
||||
}
|
||||
.code-block {
|
||||
background: #2d2d2d;
|
||||
color: #d4d4d4;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
overflow-x: auto;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 14px;
|
||||
}
|
||||
.mermaid {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
pre code.language-mermaid {
|
||||
display: block;
|
||||
background: #f8f9fa;
|
||||
padding: 15px;
|
||||
border: 2px dashed #5e72e4;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🧪 Mermaid 修复测试页面</h1>
|
||||
<p>本页面用于测试两个关键修复:</p>
|
||||
<ol>
|
||||
<li><strong>代码高亮排除 mermaid</strong>:mermaid 代码块不应被代码高亮处理</li>
|
||||
<li><strong>容器语法处理空行</strong>:容器语法中的空行应被正确保留</li>
|
||||
</ol>
|
||||
|
||||
<!-- 测试 1: 代码高亮排除 -->
|
||||
<div class="test-section">
|
||||
<h2>测试 1: 代码高亮排除 Mermaid</h2>
|
||||
|
||||
<div class="test-case">
|
||||
<h3>测试 1.1: 标准 Markdown 格式(应该被排除)</h3>
|
||||
<p class="expected">✅ 预期:不应出现代码高亮窗口,直接渲染为 Mermaid 图表</p>
|
||||
<pre><code class="language-mermaid">
|
||||
flowchart TD
|
||||
A[开始] --> B[处理]
|
||||
B --> C[结束]
|
||||
</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="test-case">
|
||||
<h3>测试 1.2: 标准 div.mermaid 格式(应该被排除)</h3>
|
||||
<p class="expected">✅ 预期:不应出现代码高亮窗口,直接渲染为 Mermaid 图表</p>
|
||||
<div class="mermaid">
|
||||
flowchart LR
|
||||
A[用户] --> B{登录?}
|
||||
B -->|是| C[显示首页]
|
||||
B -->|否| D[跳转登录页]
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="test-case">
|
||||
<h3>测试 1.3: 普通代码块(应该被高亮)</h3>
|
||||
<p class="expected">✅ 预期:应该出现代码高亮窗口</p>
|
||||
<pre><code class="language-javascript">
|
||||
function hello() {
|
||||
console.log('Hello World');
|
||||
}
|
||||
</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 测试 2: 容器语法空行处理 -->
|
||||
<div class="test-section">
|
||||
<h2>测试 2: 容器语法空行处理</h2>
|
||||
|
||||
<div class="test-case">
|
||||
<h3>测试 2.1: 包含空行的流程图</h3>
|
||||
<p class="expected">✅ 预期:空行应被保留,图表正常渲染</p>
|
||||
<div class="code-block">
|
||||
::: mermaid
|
||||
flowchart TD
|
||||
A[开始]
|
||||
|
||||
B[处理步骤1]
|
||||
|
||||
C[处理步骤2]
|
||||
|
||||
D[结束]
|
||||
|
||||
A --> B
|
||||
B --> C
|
||||
C --> D
|
||||
:::
|
||||
</div>
|
||||
<p>实际渲染:</p>
|
||||
<p>
|
||||
::: mermaid
|
||||
flowchart TD
|
||||
A[开始]
|
||||
|
||||
B[处理步骤1]
|
||||
|
||||
C[处理步骤2]
|
||||
|
||||
D[结束]
|
||||
|
||||
A --> B
|
||||
B --> C
|
||||
C --> D
|
||||
:::
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="test-case">
|
||||
<h3>测试 2.2: 包含空行的时序图</h3>
|
||||
<p class="expected">✅ 预期:空行应被保留,图表正常渲染</p>
|
||||
<div class="code-block">
|
||||
::: mermaid
|
||||
sequenceDiagram
|
||||
participant A as Alice
|
||||
participant B as Bob
|
||||
|
||||
A->>B: Hello Bob!
|
||||
|
||||
Note over A,B: 这是一个注释
|
||||
|
||||
B-->>A: Hi Alice!
|
||||
:::
|
||||
</div>
|
||||
<p>实际渲染:</p>
|
||||
<p>
|
||||
::: mermaid
|
||||
sequenceDiagram
|
||||
participant A as Alice
|
||||
participant B as Bob
|
||||
|
||||
A->>B: Hello Bob!
|
||||
|
||||
Note over A,B: 这是一个注释
|
||||
|
||||
B-->>A: Hi Alice!
|
||||
:::
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="test-case">
|
||||
<h3>测试 2.3: 包含多个连续空行</h3>
|
||||
<p class="expected">✅ 预期:多个空行应被保留,图表正常渲染</p>
|
||||
<div class="code-block">
|
||||
::: mermaid
|
||||
flowchart LR
|
||||
A[步骤A]
|
||||
|
||||
|
||||
B[步骤B]
|
||||
|
||||
|
||||
C[步骤C]
|
||||
|
||||
A --> B --> C
|
||||
:::
|
||||
</div>
|
||||
<p>实际渲染:</p>
|
||||
<p>
|
||||
::: mermaid
|
||||
flowchart LR
|
||||
A[步骤A]
|
||||
|
||||
|
||||
B[步骤B]
|
||||
|
||||
|
||||
C[步骤C]
|
||||
|
||||
A --> B --> C
|
||||
:::
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 测试 3: WP-Markdown 格式 -->
|
||||
<div class="test-section">
|
||||
<h2>测试 3: WP-Markdown 格式兼容性</h2>
|
||||
|
||||
<div class="test-case">
|
||||
<h3>测试 3.1: document.write 格式</h3>
|
||||
<p class="expected">✅ 预期:正确提取代码并渲染</p>
|
||||
<div class="mermaid">
|
||||
<script>document.write("flowchart TD\n A[开始] --> B[结束]")</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 测试结果说明 -->
|
||||
<div class="test-section">
|
||||
<h2>📋 测试检查清单</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<input type="checkbox" id="check1">
|
||||
<label for="check1">测试 1.1: Markdown 格式的 mermaid 代码块没有代码高亮窗口</label>
|
||||
</li>
|
||||
<li>
|
||||
<input type="checkbox" id="check2">
|
||||
<label for="check2">测试 1.2: div.mermaid 格式没有代码高亮窗口</label>
|
||||
</li>
|
||||
<li>
|
||||
<input type="checkbox" id="check3">
|
||||
<label for="check3">测试 1.3: 普通 JavaScript 代码有代码高亮窗口</label>
|
||||
</li>
|
||||
<li>
|
||||
<input type="checkbox" id="check4">
|
||||
<label for="check4">测试 2.1: 包含空行的流程图正常渲染</label>
|
||||
</li>
|
||||
<li>
|
||||
<input type="checkbox" id="check5">
|
||||
<label for="check5">测试 2.2: 包含空行的时序图正常渲染</label>
|
||||
</li>
|
||||
<li>
|
||||
<input type="checkbox" id="check6">
|
||||
<label for="check6">测试 2.3: 包含多个连续空行的图表正常渲染</label>
|
||||
</li>
|
||||
<li>
|
||||
<input type="checkbox" id="check7">
|
||||
<label for="check7">测试 3.1: WP-Markdown 格式正常渲染</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 简单的日志输出
|
||||
console.log('[Mermaid Test] 测试页面已加载');
|
||||
console.log('[Mermaid Test] 请检查:');
|
||||
console.log('[Mermaid Test] 1. mermaid 代码块是否被代码高亮处理');
|
||||
console.log('[Mermaid Test] 2. 容器语法中的空行是否被正确保留');
|
||||
console.log('[Mermaid Test] 3. 所有图表是否正常渲染');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,186 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Mermaid 换行符测试</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.test-case {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
h2 {
|
||||
color: #333;
|
||||
border-bottom: 2px solid #007bff;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.success {
|
||||
color: green;
|
||||
font-weight: bold;
|
||||
}
|
||||
.error {
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
}
|
||||
pre {
|
||||
background: #f4f4f4;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Mermaid 换行符测试</h1>
|
||||
<p>测试 WP-Markdown 生成的 HTML 中 <br> 标签是否正确转换为换行符</p>
|
||||
|
||||
<!-- 测试用例 1: 模拟 WP-Markdown 生成的 HTML -->
|
||||
<div class="test-case">
|
||||
<h2>测试 1: 模拟 WP-Markdown 格式(带 <br> 标签)</h2>
|
||||
<p>::: mermaid<br>
|
||||
flowchart TD<br>
|
||||
Start([开始]) --> Process[处理]<br>
|
||||
Process --> End([结束])<br>
|
||||
:::</p>
|
||||
<div id="result1"></div>
|
||||
</div>
|
||||
|
||||
<!-- 测试用例 2: 复杂流程图 -->
|
||||
<div class="test-case">
|
||||
<h2>测试 2: 复杂流程图(多行文本)</h2>
|
||||
<p>::: mermaid<br>
|
||||
flowchart TD<br>
|
||||
A[用户提交评论] --> B{启用 AI 检测?}<br>
|
||||
B -->|是| C[AI 检测]<br>
|
||||
B -->|否| D[直接保存]<br>
|
||||
C --> E{检测结果?}<br>
|
||||
E -->|垃圾评论| F[移入回收站]<br>
|
||||
E -->|正常评论| G[保存评论]<br>
|
||||
:::</p>
|
||||
<div id="result2"></div>
|
||||
</div>
|
||||
|
||||
<!-- 测试用例 3: 带样式的流程图 -->
|
||||
<div class="test-case">
|
||||
<h2>测试 3: 带样式定义</h2>
|
||||
<p>::: mermaid<br>
|
||||
flowchart LR<br>
|
||||
A[开始] --> B[处理]<br>
|
||||
B --> C[结束]<br>
|
||||
style A fill:#e1f5e1,stroke:#2e7d32<br>
|
||||
style C fill:#ffe1e1,stroke:#c62828<br>
|
||||
:::</p>
|
||||
<div id="result3"></div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
||||
<script>
|
||||
// 模拟 Argon 主题的 htmlToText 函数
|
||||
function htmlToText(html) {
|
||||
// 将 <br> 和 <br/> 转换为换行符
|
||||
let text = html.replace(/<br\s*\/?>/gi, '\n');
|
||||
// 移除其他 HTML 标签
|
||||
text = text.replace(/<[^>]+>/g, '');
|
||||
// 解码 HTML 实体
|
||||
text = text
|
||||
.replace(/ /g, ' ')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'");
|
||||
return text;
|
||||
}
|
||||
|
||||
// 提取容器语法内容
|
||||
function extractContainerContent(element) {
|
||||
let html = element.innerHTML;
|
||||
let text = element.textContent.trim();
|
||||
|
||||
console.log('[测试] 原始 HTML:', html);
|
||||
console.log('[测试] 原始文本:', text);
|
||||
|
||||
if (!text.startsWith('::: mermaid')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 使用 htmlToText 转换
|
||||
let content = htmlToText(html);
|
||||
console.log('[测试] 转换后文本:', content);
|
||||
|
||||
// 移除开始和结束标记
|
||||
content = content.replace(/^:::\s*mermaid\s*/i, '').trim();
|
||||
content = content.replace(/:::\s*$/, '').trim();
|
||||
|
||||
console.log('[测试] 最终代码:', content);
|
||||
return content;
|
||||
}
|
||||
|
||||
// 测试函数
|
||||
function testMermaid(testId, resultId) {
|
||||
const element = document.querySelector(`#${testId}`).previousElementSibling;
|
||||
const resultDiv = document.getElementById(resultId);
|
||||
|
||||
try {
|
||||
const code = extractContainerContent(element);
|
||||
|
||||
if (!code) {
|
||||
resultDiv.innerHTML = '<p class="error">❌ 提取失败:未找到代码</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查换行符
|
||||
const lines = code.split('\n');
|
||||
console.log(`[测试 ${testId}] 提取到 ${lines.length} 行代码`);
|
||||
|
||||
// 渲染图表
|
||||
mermaid.render(`mermaid-${testId}`, code).then(result => {
|
||||
resultDiv.innerHTML = `
|
||||
<p class="success">✅ 渲染成功</p>
|
||||
<p>提取到 ${lines.length} 行代码</p>
|
||||
<details>
|
||||
<summary>查看提取的代码</summary>
|
||||
<pre>${code}</pre>
|
||||
</details>
|
||||
${result.svg}
|
||||
`;
|
||||
}).catch(error => {
|
||||
resultDiv.innerHTML = `
|
||||
<p class="error">❌ 渲染失败: ${error.message}</p>
|
||||
<details>
|
||||
<summary>查看提取的代码</summary>
|
||||
<pre>${code}</pre>
|
||||
</details>
|
||||
`;
|
||||
});
|
||||
} catch (error) {
|
||||
resultDiv.innerHTML = `<p class="error">❌ 错误: ${error.message}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
theme: 'default',
|
||||
securityLevel: 'loose'
|
||||
});
|
||||
|
||||
// 运行测试
|
||||
testMermaid('test1', 'result1');
|
||||
testMermaid('test2', 'result2');
|
||||
testMermaid('test3', 'result3');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,383 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Mermaid 图表尺寸优化测试</title>
|
||||
<link rel="stylesheet" href="../style.css">
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.test-section {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
.test-section h2 {
|
||||
color: #5e72e4;
|
||||
border-bottom: 2px solid #5e72e4;
|
||||
padding-bottom: 10px;
|
||||
margin-top: 0;
|
||||
}
|
||||
.test-case {
|
||||
margin: 20px 0;
|
||||
padding: 15px;
|
||||
background: #f8f9fa;
|
||||
border-left: 4px solid #5e72e4;
|
||||
}
|
||||
.test-case h3 {
|
||||
margin-top: 0;
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
}
|
||||
.expected {
|
||||
color: #28a745;
|
||||
font-weight: bold;
|
||||
margin: 10px 0;
|
||||
}
|
||||
.info {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
margin: 5px 0;
|
||||
}
|
||||
.toggle-theme {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
padding: 10px 20px;
|
||||
background: #5e72e4;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
z-index: 1000;
|
||||
}
|
||||
.toggle-theme:hover {
|
||||
background: #4c63d2;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<button class="toggle-theme" onclick="toggleTheme()">🌓 切换夜间模式</button>
|
||||
|
||||
<h1>🎨 Mermaid 图表尺寸优化测试</h1>
|
||||
<p>本页面用于测试 Mermaid 图表的尺寸优化,确保低内容图表不会显示过大。</p>
|
||||
|
||||
<!-- 测试 1: 极简图表 -->
|
||||
<div class="test-section">
|
||||
<h2>测试 1: 极简图表(2-3个节点)</h2>
|
||||
|
||||
<div class="test-case">
|
||||
<h3>1.1 简单流程图(2个节点)</h3>
|
||||
<p class="expected">✅ 预期:图表高度不超过 600px,居中显示</p>
|
||||
<p class="info">优化前:可能显示超大,占据整个屏幕</p>
|
||||
<div class="mermaid-container">
|
||||
<div class="mermaid">
|
||||
flowchart TD
|
||||
A[开始] --> B[结束]
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="test-case">
|
||||
<h3>1.2 简单流程图(3个节点)</h3>
|
||||
<p class="expected">✅ 预期:图表高度不超过 600px,居中显示</p>
|
||||
<div class="mermaid-container">
|
||||
<div class="mermaid">
|
||||
flowchart LR
|
||||
A[输入] --> B[处理] --> C[输出]
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="test-case">
|
||||
<h3>1.3 简单判断流程</h3>
|
||||
<p class="expected">✅ 预期:图表高度不超过 600px,居中显示</p>
|
||||
<div class="mermaid-container">
|
||||
<div class="mermaid">
|
||||
flowchart TD
|
||||
A[用户] --> B{登录?}
|
||||
B -->|是| C[首页]
|
||||
B -->|否| D[登录页]
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 测试 2: 中等复杂度图表 -->
|
||||
<div class="test-section">
|
||||
<h2>测试 2: 中等复杂度图表(5-10个节点)</h2>
|
||||
|
||||
<div class="test-case">
|
||||
<h3>2.1 标准流程图</h3>
|
||||
<p class="expected">✅ 预期:图表高度不超过 600px,自适应内容</p>
|
||||
<div class="mermaid-container">
|
||||
<div class="mermaid">
|
||||
flowchart TD
|
||||
A[开始] --> B[输入数据]
|
||||
B --> C{数据有效?}
|
||||
C -->|是| D[处理数据]
|
||||
C -->|否| E[显示错误]
|
||||
D --> F[保存结果]
|
||||
E --> G[结束]
|
||||
F --> G
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="test-case">
|
||||
<h3>2.2 时序图</h3>
|
||||
<p class="expected">✅ 预期:图表高度不超过 600px</p>
|
||||
<div class="mermaid-container">
|
||||
<div class="mermaid">
|
||||
sequenceDiagram
|
||||
participant U as 用户
|
||||
participant F as 前端
|
||||
participant B as 后端
|
||||
participant D as 数据库
|
||||
|
||||
U->>F: 提交表单
|
||||
F->>B: 发送请求
|
||||
B->>D: 查询数据
|
||||
D-->>B: 返回结果
|
||||
B-->>F: 返回响应
|
||||
F-->>U: 显示结果
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 测试 3: 复杂图表 -->
|
||||
<div class="test-section">
|
||||
<h2>测试 3: 复杂图表(10+个节点)</h2>
|
||||
|
||||
<div class="test-case">
|
||||
<h3>3.1 复杂流程图</h3>
|
||||
<p class="expected">✅ 预期:图表高度限制在 600px,出现滚动条</p>
|
||||
<p class="info">优化:超过最大高度时,容器会出现垂直滚动条</p>
|
||||
<div class="mermaid-container">
|
||||
<div class="mermaid">
|
||||
flowchart TD
|
||||
A[开始] --> B[初始化]
|
||||
B --> C{检查权限}
|
||||
C -->|有权限| D[加载配置]
|
||||
C -->|无权限| E[显示错误]
|
||||
D --> F{配置有效?}
|
||||
F -->|是| G[连接数据库]
|
||||
F -->|否| H[使用默认配置]
|
||||
G --> I{连接成功?}
|
||||
I -->|是| J[加载数据]
|
||||
I -->|否| K[重试连接]
|
||||
K --> L{重试次数<3?}
|
||||
L -->|是| G
|
||||
L -->|否| M[连接失败]
|
||||
H --> J
|
||||
J --> N[处理数据]
|
||||
N --> O{处理成功?}
|
||||
O -->|是| P[显示结果]
|
||||
O -->|否| Q[显示错误]
|
||||
P --> R[记录日志]
|
||||
Q --> R
|
||||
E --> R
|
||||
M --> R
|
||||
R --> S[结束]
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="test-case">
|
||||
<h3>3.2 类图</h3>
|
||||
<p class="expected">✅ 预期:图表高度限制在 600px</p>
|
||||
<div class="mermaid-container">
|
||||
<div class="mermaid">
|
||||
classDiagram
|
||||
class Animal {
|
||||
+String name
|
||||
+int age
|
||||
+makeSound()
|
||||
+eat()
|
||||
}
|
||||
class Dog {
|
||||
+String breed
|
||||
+bark()
|
||||
+fetch()
|
||||
}
|
||||
class Cat {
|
||||
+String color
|
||||
+meow()
|
||||
+scratch()
|
||||
}
|
||||
class Bird {
|
||||
+boolean canFly
|
||||
+sing()
|
||||
+fly()
|
||||
}
|
||||
Animal <|-- Dog
|
||||
Animal <|-- Cat
|
||||
Animal <|-- Bird
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 测试 4: 横向图表 -->
|
||||
<div class="test-section">
|
||||
<h2>测试 4: 横向图表(宽度测试)</h2>
|
||||
|
||||
<div class="test-case">
|
||||
<h3>4.1 横向流程图</h3>
|
||||
<p class="expected">✅ 预期:宽度自适应,出现横向滚动条</p>
|
||||
<div class="mermaid-container">
|
||||
<div class="mermaid">
|
||||
flowchart LR
|
||||
A[步骤1] --> B[步骤2] --> C[步骤3] --> D[步骤4] --> E[步骤5]
|
||||
E --> F[步骤6] --> G[步骤7] --> H[步骤8] --> I[步骤9] --> J[步骤10]
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 测试 5: 响应式测试 -->
|
||||
<div class="test-section">
|
||||
<h2>测试 5: 响应式适配</h2>
|
||||
|
||||
<div class="test-case">
|
||||
<h3>5.1 移动端适配</h3>
|
||||
<p class="expected">✅ 预期:在移动端(<768px)高度限制为 500px</p>
|
||||
<p class="info">请调整浏览器窗口大小测试</p>
|
||||
<div class="mermaid-container">
|
||||
<div class="mermaid">
|
||||
flowchart TD
|
||||
A[移动端] --> B[平板]
|
||||
B --> C[桌面端]
|
||||
A --> D[响应式]
|
||||
B --> D
|
||||
C --> D
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 测试结果检查清单 -->
|
||||
<div class="test-section">
|
||||
<h2>📋 测试检查清单</h2>
|
||||
<ul style="list-style: none; padding: 0;">
|
||||
<li style="margin: 10px 0;">
|
||||
<input type="checkbox" id="check1">
|
||||
<label for="check1">✅ 极简图表(2-3节点)不会显示过大,高度合理</label>
|
||||
</li>
|
||||
<li style="margin: 10px 0;">
|
||||
<input type="checkbox" id="check2">
|
||||
<label for="check2">✅ 图表在容器中居中显示</label>
|
||||
</li>
|
||||
<li style="margin: 10px 0;">
|
||||
<input type="checkbox" id="check3">
|
||||
<label for="check3">✅ 中等复杂度图表显示正常,高度不超过 600px</label>
|
||||
</li>
|
||||
<li style="margin: 10px 0;">
|
||||
<input type="checkbox" id="check4">
|
||||
<label for="check4">✅ 复杂图表高度限制在 600px,出现滚动条</label>
|
||||
</li>
|
||||
<li style="margin: 10px 0;">
|
||||
<input type="checkbox" id="check5">
|
||||
<label for="check5">✅ 横向图表宽度自适应,出现横向滚动条</label>
|
||||
</li>
|
||||
<li style="margin: 10px 0;">
|
||||
<input type="checkbox" id="check6">
|
||||
<label for="check6">✅ 夜间模式下图表显示正常</label>
|
||||
</li>
|
||||
<li style="margin: 10px 0;">
|
||||
<input type="checkbox" id="check7">
|
||||
<label for="check7">✅ 移动端(<768px)高度限制为 500px</label>
|
||||
</li>
|
||||
<li style="margin: 10px 0;">
|
||||
<input type="checkbox" id="check8">
|
||||
<label for="check8">✅ 小屏幕(<480px)高度限制为 400px</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 优化说明 -->
|
||||
<div class="test-section">
|
||||
<h2>🎯 优化说明</h2>
|
||||
<h3>主要改进:</h3>
|
||||
<ol>
|
||||
<li><strong>最大高度限制</strong>:SVG 最大高度设置为 600px(桌面端)</li>
|
||||
<li><strong>居中显示</strong>:使用 flexbox 让图表在容器中居中</li>
|
||||
<li><strong>宽度自适应</strong>:使用 <code>width: auto !important</code> 让图表保持原始宽高比</li>
|
||||
<li><strong>响应式适配</strong>:
|
||||
<ul>
|
||||
<li>平板(<768px):最大高度 500px</li>
|
||||
<li>手机(<480px):最大高度 400px</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>最小高度</strong>:容器最小高度 100px,避免空白过小</li>
|
||||
</ol>
|
||||
|
||||
<h3>CSS 关键属性:</h3>
|
||||
<pre style="background: #f8f9fa; padding: 15px; border-radius: 4px; overflow-x: auto;"><code>.mermaid-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.mermaid-container svg {
|
||||
max-width: 100%;
|
||||
max-height: 600px;
|
||||
height: auto;
|
||||
width: auto !important;
|
||||
}</code></pre>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
||||
<script>
|
||||
// 初始化 Mermaid
|
||||
mermaid.initialize({
|
||||
startOnLoad: true,
|
||||
theme: 'default',
|
||||
securityLevel: 'loose',
|
||||
flowchart: {
|
||||
useMaxWidth: false,
|
||||
htmlLabels: true
|
||||
}
|
||||
});
|
||||
|
||||
// 切换夜间模式
|
||||
function toggleTheme() {
|
||||
document.documentElement.classList.toggle('darkmode');
|
||||
const isDark = document.documentElement.classList.contains('darkmode');
|
||||
|
||||
// 重新初始化 Mermaid 以应用新主题
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
theme: isDark ? 'dark' : 'default',
|
||||
securityLevel: 'loose',
|
||||
flowchart: {
|
||||
useMaxWidth: false,
|
||||
htmlLabels: true
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`[Theme] 切换到 ${isDark ? '夜间' : '日间'} 模式`);
|
||||
}
|
||||
|
||||
// 页面加载完成后的日志
|
||||
window.addEventListener('load', function() {
|
||||
console.log('[Mermaid Size Test] 测试页面已加载');
|
||||
console.log('[Mermaid Size Test] 请检查:');
|
||||
console.log('[Mermaid Size Test] 1. 极简图表是否显示合理大小');
|
||||
console.log('[Mermaid Size Test] 2. 图表是否在容器中居中');
|
||||
console.log('[Mermaid Size Test] 3. 复杂图表是否有高度限制');
|
||||
console.log('[Mermaid Size Test] 4. 响应式适配是否正常');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,365 +0,0 @@
|
||||
# AI 垃圾评论检测系统架构
|
||||
|
||||
## 系统概述
|
||||
|
||||
本文档展示 Argon 主题的 AI 垃圾评论检测系统的完整架构和流程。
|
||||
|
||||
## 主流程图
|
||||
|
||||
[mermaid theme="default" width="100%" align="center"]
|
||||
flowchart TD
|
||||
Start([用户提交评论]) --> PreProcess[预处理评论]
|
||||
PreProcess --> CheckEnabled{启用 AI 检测?}
|
||||
|
||||
CheckEnabled -->|否| DirectSave[直接保存评论]
|
||||
CheckEnabled -->|是| CheckMode{检测模式?}
|
||||
|
||||
CheckMode -->|manual| DirectSave
|
||||
CheckMode -->|keyword| CheckKeyword[检查关键字]
|
||||
CheckMode -->|sample| CheckSample[智能抽样]
|
||||
CheckMode -->|all| NeedCheck[需要检测]
|
||||
|
||||
CheckKeyword --> KeywordMatch{匹配关键字?}
|
||||
KeywordMatch -->|是| NeedCheck
|
||||
KeywordMatch -->|否| DirectSave
|
||||
|
||||
CheckSample --> SampleDecision{抽中?}
|
||||
SampleDecision -->|是| NeedCheck
|
||||
SampleDecision -->|否| DirectSave
|
||||
|
||||
NeedCheck --> SetPending[设置待审核状态]
|
||||
SetPending --> SaveWithFlag[保存评论并标记]
|
||||
SaveWithFlag --> TriggerDetect[触发 AI 检测]
|
||||
|
||||
TriggerDetect --> CallAPI[调用 AI API]
|
||||
CallAPI --> ParseResult[解析结果]
|
||||
|
||||
ParseResult --> CheckConfidence{置信度?}
|
||||
CheckConfidence -->|高| AutoHandle[自动处理]
|
||||
CheckConfidence -->|中| ManualReview[人工审核]
|
||||
CheckConfidence -->|低| MarkNormal[标记正常]
|
||||
|
||||
AutoHandle --> CheckAction{处理方式?}
|
||||
CheckAction -->|trash| MoveTrash[移入回收站]
|
||||
CheckAction -->|hold| KeepPending[保持待审核]
|
||||
CheckAction -->|mark| JustMark[仅标记]
|
||||
|
||||
MoveTrash --> NotifyAdmin[通知管理员]
|
||||
KeepPending --> NotifyAdmin
|
||||
JustMark --> SaveResult[保存结果]
|
||||
ManualReview --> SaveResult
|
||||
MarkNormal --> SaveResult
|
||||
NotifyAdmin --> SaveResult
|
||||
|
||||
SaveResult --> AILearn{启用学习?}
|
||||
AILearn -->|是| ExtractKeywords[提取关键词]
|
||||
AILearn -->|否| End([结束])
|
||||
ExtractKeywords --> UpdateDB[更新词库]
|
||||
UpdateDB --> End
|
||||
|
||||
DirectSave --> End
|
||||
|
||||
style Start fill:#e1f5e1,stroke:#2e7d32,stroke-width:2px
|
||||
style End fill:#e1f5e1,stroke:#2e7d32,stroke-width:2px
|
||||
style MoveTrash fill:#ff6b6b,stroke:#c62828,stroke-width:2px
|
||||
style MarkNormal fill:#95e1d3,stroke:#2e7d32,stroke-width:2px
|
||||
style ManualReview fill:#ffa500,stroke:#ff8f00,stroke-width:2px
|
||||
[/mermaid]
|
||||
|
||||
## 检测模式详解
|
||||
|
||||
[mermaid theme="default" width="80%" align="center"]
|
||||
flowchart LR
|
||||
subgraph Manual[Manual 模式]
|
||||
M1[关闭实时检测]
|
||||
M2[仅手动扫描]
|
||||
end
|
||||
|
||||
subgraph Keyword[Keyword 模式]
|
||||
K1[关键字触发]
|
||||
K2[精准检测]
|
||||
K3[低成本]
|
||||
end
|
||||
|
||||
subgraph Sample[Sample 模式]
|
||||
S1[智能抽样]
|
||||
S2[平衡准确性]
|
||||
S3[控制成本]
|
||||
end
|
||||
|
||||
subgraph All[All 模式]
|
||||
A1[全量检测]
|
||||
A2[最高准确性]
|
||||
A3[高成本]
|
||||
end
|
||||
|
||||
style Manual fill:#e3f2fd
|
||||
style Keyword fill:#fff3e0
|
||||
style Sample fill:#f3e5f5
|
||||
style All fill:#fce4ec
|
||||
[/mermaid]
|
||||
|
||||
## AI 检测流程
|
||||
|
||||
[mermaid theme="default" width="90%"]
|
||||
sequenceDiagram
|
||||
participant User as 用户
|
||||
participant WP as WordPress
|
||||
participant Argon as Argon 主题
|
||||
participant AI as AI API
|
||||
participant DB as 数据库
|
||||
|
||||
User->>WP: 提交评论
|
||||
WP->>Argon: 触发 preprocess_comment
|
||||
Argon->>Argon: 检查检测规则
|
||||
|
||||
alt 需要检测
|
||||
Argon->>DB: 保存评论(待审核)
|
||||
Argon->>AI: 发送检测请求
|
||||
AI-->>Argon: 返回检测结果
|
||||
Argon->>Argon: 解析结果和置信度
|
||||
|
||||
alt 高置信度垃圾评论
|
||||
Argon->>DB: 移入回收站
|
||||
Argon->>WP: 发送通知邮件
|
||||
else 中等置信度
|
||||
Argon->>DB: 标记待人工审核
|
||||
else 正常评论
|
||||
Argon->>DB: 标记为正常
|
||||
end
|
||||
|
||||
Argon->>DB: 保存检测记录
|
||||
|
||||
opt 启用 AI 学习
|
||||
Argon->>AI: 提取关键词
|
||||
AI-->>Argon: 返回关键词
|
||||
Argon->>DB: 更新学习词库
|
||||
end
|
||||
else 跳过检测
|
||||
Argon->>DB: 直接保存评论
|
||||
end
|
||||
|
||||
WP-->>User: 显示提交结果
|
||||
[/mermaid]
|
||||
|
||||
## 数据库结构
|
||||
|
||||
[mermaid theme="default" width="85%"]
|
||||
erDiagram
|
||||
COMMENT ||--o{ COMMENT_META : has
|
||||
COMMENT {
|
||||
bigint comment_ID PK
|
||||
bigint comment_post_ID FK
|
||||
text comment_content
|
||||
varchar comment_author
|
||||
varchar comment_author_email
|
||||
varchar comment_author_IP
|
||||
datetime comment_date
|
||||
varchar comment_approved
|
||||
}
|
||||
|
||||
COMMENT_META {
|
||||
bigint meta_id PK
|
||||
bigint comment_id FK
|
||||
varchar meta_key
|
||||
longtext meta_value
|
||||
}
|
||||
|
||||
SPAM_DETECTION ||--|| COMMENT : detects
|
||||
SPAM_DETECTION {
|
||||
bigint id PK
|
||||
bigint comment_id FK
|
||||
varchar result
|
||||
float confidence
|
||||
text reason
|
||||
text keywords
|
||||
datetime detected_at
|
||||
varchar detection_code
|
||||
}
|
||||
|
||||
LEARNED_KEYWORDS ||--o{ SPAM_DETECTION : learns_from
|
||||
LEARNED_KEYWORDS {
|
||||
bigint id PK
|
||||
varchar keyword
|
||||
int weight
|
||||
varchar category
|
||||
datetime created_at
|
||||
datetime updated_at
|
||||
}
|
||||
[/mermaid]
|
||||
|
||||
## 系统状态机
|
||||
|
||||
[mermaid theme="default" width="70%"]
|
||||
stateDiagram-v2
|
||||
[*] --> Submitted: 用户提交
|
||||
|
||||
Submitted --> Pending: 需要检测
|
||||
Submitted --> Approved: 跳过检测
|
||||
|
||||
Pending --> Detecting: 开始检测
|
||||
Detecting --> Analyzed: 检测完成
|
||||
|
||||
Analyzed --> Spam: 高置信度垃圾
|
||||
Analyzed --> Suspicious: 中等置信度
|
||||
Analyzed --> Clean: 低置信度/正常
|
||||
|
||||
Spam --> Trash: 自动处理
|
||||
Spam --> Hold: 保持待审核
|
||||
Spam --> Marked: 仅标记
|
||||
|
||||
Suspicious --> ManualReview: 等待人工审核
|
||||
Clean --> Approved: 自动通过
|
||||
|
||||
ManualReview --> Approved: 管理员批准
|
||||
ManualReview --> Trash: 管理员拒绝
|
||||
|
||||
Hold --> Approved: 管理员批准
|
||||
Hold --> Trash: 管理员拒绝
|
||||
|
||||
Marked --> Approved: 管理员批准
|
||||
Marked --> Trash: 管理员拒绝
|
||||
|
||||
Trash --> [*]
|
||||
Approved --> [*]
|
||||
|
||||
note right of Detecting
|
||||
调用 AI API
|
||||
解析返回结果
|
||||
计算置信度
|
||||
end note
|
||||
|
||||
note right of ManualReview
|
||||
显示在后台
|
||||
等待管理员操作
|
||||
end note
|
||||
[/mermaid]
|
||||
|
||||
## 性能优化策略
|
||||
|
||||
[mermaid theme="default" width="95%"]
|
||||
flowchart TD
|
||||
subgraph Input[输入优化]
|
||||
I1[关键字预过滤]
|
||||
I2[白名单用户]
|
||||
I3[智能抽样]
|
||||
end
|
||||
|
||||
subgraph Process[处理优化]
|
||||
P1[异步检测]
|
||||
P2[批量处理]
|
||||
P3[缓存结果]
|
||||
end
|
||||
|
||||
subgraph API[API 优化]
|
||||
A1[Prompt 优化]
|
||||
A2[Token 控制]
|
||||
A3[超时处理]
|
||||
end
|
||||
|
||||
subgraph Storage[存储优化]
|
||||
S1[索引优化]
|
||||
S2[定期清理]
|
||||
S3[归档历史]
|
||||
end
|
||||
|
||||
Input --> Process
|
||||
Process --> API
|
||||
API --> Storage
|
||||
|
||||
style Input fill:#e8f5e9
|
||||
style Process fill:#e3f2fd
|
||||
style API fill:#fff3e0
|
||||
style Storage fill:#f3e5f5
|
||||
[/mermaid]
|
||||
|
||||
## 配置建议矩阵
|
||||
|
||||
[mermaid theme="default" width="100%"]
|
||||
graph TB
|
||||
subgraph Small[小型博客 < 100评论/天]
|
||||
S1[检测模式: keyword]
|
||||
S2[Prompt: 标准模式]
|
||||
S3[置信度阈值: > 0.9]
|
||||
S4[自动处理: trash]
|
||||
end
|
||||
|
||||
subgraph Medium[中型博客 100-500评论/天]
|
||||
M1[检测模式: sample 30%]
|
||||
M2[Prompt: 标准模式]
|
||||
M3[置信度阈值: > 0.85]
|
||||
M4[自动处理: hold]
|
||||
end
|
||||
|
||||
subgraph Large[大型博客 > 500评论/天]
|
||||
L1[检测模式: sample 40%]
|
||||
L2[Prompt: 极简模式]
|
||||
L3[置信度阈值: > 0.8]
|
||||
L4[自动处理: mark]
|
||||
L5[定期批量扫描]
|
||||
end
|
||||
|
||||
style Small fill:#c8e6c9
|
||||
style Medium fill:#fff9c4
|
||||
style Large fill:#ffccbc
|
||||
[/mermaid]
|
||||
|
||||
## 错误处理流程
|
||||
|
||||
[mermaid theme="default" width="85%"]
|
||||
flowchart TD
|
||||
Start([检测开始]) --> CallAPI[调用 AI API]
|
||||
|
||||
CallAPI --> CheckResponse{响应状态?}
|
||||
|
||||
CheckResponse -->|成功| ParseJSON[解析 JSON]
|
||||
CheckResponse -->|超时| Timeout[超时处理]
|
||||
CheckResponse -->|错误| APIError[API 错误]
|
||||
|
||||
ParseJSON --> ValidateData{数据有效?}
|
||||
ValidateData -->|是| ProcessResult[处理结果]
|
||||
ValidateData -->|否| DataError[数据错误]
|
||||
|
||||
Timeout --> RetryCheck{重试次数?}
|
||||
RetryCheck -->|< 3| Retry[重试请求]
|
||||
RetryCheck -->|>= 3| FallbackPending[降级:待审核]
|
||||
|
||||
Retry --> CallAPI
|
||||
|
||||
APIError --> LogError[记录错误日志]
|
||||
DataError --> LogError
|
||||
|
||||
LogError --> NotifyAdmin[通知管理员]
|
||||
NotifyAdmin --> FallbackPending
|
||||
|
||||
ProcessResult --> SaveResult[保存结果]
|
||||
FallbackPending --> SaveResult
|
||||
|
||||
SaveResult --> End([结束])
|
||||
|
||||
style Timeout fill:#ffccbc
|
||||
style APIError fill:#ffccbc
|
||||
style DataError fill:#ffccbc
|
||||
style FallbackPending fill:#fff9c4
|
||||
style ProcessResult fill:#c8e6c9
|
||||
[/mermaid]
|
||||
|
||||
## 总结
|
||||
|
||||
通过以上流程图,我们可以清晰地看到:
|
||||
|
||||
1. **主流程**:从用户提交到最终处理的完整流程
|
||||
2. **检测模式**:四种模式的特点和适用场景
|
||||
3. **时序交互**:各组件之间的交互顺序
|
||||
4. **数据结构**:数据库表关系和字段定义
|
||||
5. **状态机**:评论的各种状态转换
|
||||
6. **性能优化**:多层次的优化策略
|
||||
7. **配置建议**:不同规模博客的推荐配置
|
||||
8. **错误处理**:完善的异常处理机制
|
||||
|
||||
这个系统设计充分考虑了:
|
||||
- ✅ 准确性:多级置信度判断
|
||||
- ✅ 性能:异步处理、智能抽样
|
||||
- ✅ 成本:灵活的检测模式
|
||||
- ✅ 可靠性:完善的错误处理
|
||||
- ✅ 可维护性:清晰的架构设计
|
||||
@@ -1,197 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>单元素容器语法测试</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.test-case {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
h2 {
|
||||
color: #333;
|
||||
border-bottom: 2px solid #007bff;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.success {
|
||||
color: green;
|
||||
font-weight: bold;
|
||||
}
|
||||
.error {
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
}
|
||||
pre {
|
||||
background: #f4f4f4;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
overflow-x: auto;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>单元素容器语法测试</h1>
|
||||
<p>测试 WP-Markdown 将整个容器语法放在一个 <p> 元素中的情况</p>
|
||||
|
||||
<!-- 测试用例 1: 简单流程图(单元素) -->
|
||||
<div class="test-case">
|
||||
<h2>测试 1: 简单流程图(单元素,带全角箭头)</h2>
|
||||
<p>::: mermaid<br>flowchart TD<br> Start([开始]) –> Process[处理]<br> Process –> End([结束])<br>:::</p>
|
||||
<div id="result1"></div>
|
||||
</div>
|
||||
|
||||
<!-- 测试用例 2: 带样式的流程图(单元素) -->
|
||||
<div class="test-case">
|
||||
<h2>测试 2: 带样式定义(单元素)</h2>
|
||||
<p>::: mermaid<br>flowchart LR<br> A[开始] –> B[处理]<br> B –> C[结束]<br> style A fill:#e1f5e1,stroke:#2e7d32<br> style C fill:#ffe1e1,stroke:#c62828<br>:::</p>
|
||||
<div id="result2"></div>
|
||||
</div>
|
||||
|
||||
<!-- 测试用例 3: 复杂流程图(单元素,模拟 AI 评论审核) -->
|
||||
<div class="test-case">
|
||||
<h2>测试 3: 复杂流程图(单元素,多个节点)</h2>
|
||||
<p>::: mermaid<br>flowchart TD<br> Start([用户提交评论]) –> PreProcess[预处理]<br> PreProcess –> CheckEnabled{启用 AI 检测?}<br> CheckEnabled –>|否| SaveComment[保存评论]<br> CheckEnabled –>|是| CheckMode{检测模式?}<br> CheckMode –>|manual| SaveComment<br> CheckMode –>|keyword/sample/all| AICheck[AI 检测]<br> AICheck –> CheckResult{检测结果?}<br> CheckResult –>|垃圾评论| Trash[移入回收站]<br> CheckResult –>|正常评论| SaveComment<br> style Start fill:#e1f5e1,stroke:#2e7d32<br> style Trash fill:#ff6b6b,stroke:#c62828<br>:::</p>
|
||||
<div id="result3"></div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
||||
<script>
|
||||
// 模拟 Argon 主题的 htmlToText 函数(增强版)
|
||||
function htmlToText(html) {
|
||||
// 将 <br> 和 <br/> 转换为换行符
|
||||
let text = html.replace(/<br\s*\/?>/gi, '\n');
|
||||
// 移除其他 HTML 标签
|
||||
text = text.replace(/<[^>]+>/g, '');
|
||||
// 解码 HTML 实体
|
||||
text = text
|
||||
.replace(/ /g, ' ')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'")
|
||||
.replace(/–/g, '-') // EN DASH
|
||||
.replace(/—/g, '--') // EM DASH
|
||||
.replace(/→/g, '->') // RIGHTWARDS ARROW
|
||||
.replace(/–/g, '-') // EN DASH (named entity)
|
||||
.replace(/—/g, '--') // EM DASH (named entity)
|
||||
.replace(/→/g, '->'); // RIGHTWARDS ARROW (named entity)
|
||||
|
||||
// 转换 Unicode 字符(WordPress 可能直接输出 Unicode)
|
||||
text = text
|
||||
.replace(/–/g, '-') // U+2013 EN DASH
|
||||
.replace(/—/g, '--') // U+2014 EM DASH
|
||||
.replace(/→/g, '->'); // U+2192 RIGHTWARDS ARROW
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
// 提取单元素容器语法内容
|
||||
function extractSingleElementContainer(element) {
|
||||
let html = element.innerHTML;
|
||||
let text = element.textContent.trim();
|
||||
|
||||
console.log('[测试] 原始 HTML:', html.substring(0, 200));
|
||||
console.log('[测试] 原始文本:', text.substring(0, 200));
|
||||
|
||||
if (!text.startsWith('::: mermaid')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查是否整个内容都在一个元素中
|
||||
if (text.includes(':::') && text.lastIndexOf(':::') > 10) {
|
||||
console.log('[测试] 检测到单元素容器语法');
|
||||
|
||||
// 使用 htmlToText 转换
|
||||
let fullText = htmlToText(html);
|
||||
console.log('[测试] 转换后文本:', fullText.substring(0, 200));
|
||||
|
||||
// 移除开始和结束标记
|
||||
fullText = fullText.replace(/^:::\s*mermaid\s*/i, '').trim();
|
||||
fullText = fullText.replace(/:::\s*$/, '').trim();
|
||||
|
||||
console.log('[测试] 最终代码:', fullText);
|
||||
return fullText;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 测试函数
|
||||
function testMermaid(testId, resultId) {
|
||||
const element = document.querySelector(`#${testId}`).previousElementSibling;
|
||||
const resultDiv = document.getElementById(resultId);
|
||||
|
||||
try {
|
||||
const code = extractSingleElementContainer(element);
|
||||
|
||||
if (!code) {
|
||||
resultDiv.innerHTML = '<p class="error">❌ 提取失败:未找到代码</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查换行符
|
||||
const lines = code.split('\n');
|
||||
console.log(`[测试 ${testId}] 提取到 ${lines.length} 行代码`);
|
||||
|
||||
// 检查箭头符号
|
||||
const hasCorrectArrows = code.includes('-->') || code.includes('->');
|
||||
const hasWrongArrows = code.includes('–>') || code.includes('→');
|
||||
|
||||
// 渲染图表
|
||||
mermaid.render(`mermaid-${testId}`, code).then(result => {
|
||||
resultDiv.innerHTML = `
|
||||
<p class="success">✅ 渲染成功</p>
|
||||
<p>提取到 ${lines.length} 行代码</p>
|
||||
<p>箭头符号: ${hasCorrectArrows ? '✅ 正确 (-->)' : '❌ 错误'}</p>
|
||||
<p>全角箭头: ${hasWrongArrows ? '❌ 存在 (–>)' : '✅ 不存在'}</p>
|
||||
<details>
|
||||
<summary>查看提取的代码</summary>
|
||||
<pre>${code}</pre>
|
||||
</details>
|
||||
${result.svg}
|
||||
`;
|
||||
}).catch(error => {
|
||||
resultDiv.innerHTML = `
|
||||
<p class="error">❌ 渲染失败: ${error.message}</p>
|
||||
<p>箭头符号: ${hasCorrectArrows ? '✅ 正确 (-->)' : '❌ 错误'}</p>
|
||||
<p>全角箭头: ${hasWrongArrows ? '❌ 存在 (–>)' : '✅ 不存在'}</p>
|
||||
<details>
|
||||
<summary>查看提取的代码</summary>
|
||||
<pre>${code}</pre>
|
||||
</details>
|
||||
`;
|
||||
});
|
||||
} catch (error) {
|
||||
resultDiv.innerHTML = `<p class="error">❌ 错误: ${error.message}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
theme: 'default',
|
||||
securityLevel: 'loose'
|
||||
});
|
||||
|
||||
// 运行测试
|
||||
testMermaid('test1', 'result1');
|
||||
testMermaid('test2', 'result2');
|
||||
testMermaid('test3', 'result3');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,214 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>WP-Markdown Mermaid 格式测试</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.test-case {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
h2 {
|
||||
color: #333;
|
||||
border-bottom: 2px solid #007bff;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.mermaid {
|
||||
background: #f9f9f9;
|
||||
padding: 15px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
pre {
|
||||
background: #f4f4f4;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>WP-Markdown Mermaid 格式测试</h1>
|
||||
<p>本页面测试 WP-Markdown 插件生成的各种 Mermaid 代码块格式</p>
|
||||
|
||||
<!-- 测试用例 1: 标准 document.write 格式(单行) -->
|
||||
<div class="test-case">
|
||||
<h2>测试 1: 标准 document.write 格式(单行)</h2>
|
||||
<div class="mermaid">
|
||||
<script>document.write("flowchart TD\nA[开始] --> B[处理]\nB --> C[结束]")</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 测试用例 2: document.write 格式(多行,带转义) -->
|
||||
<div class="test-case">
|
||||
<h2>测试 2: document.write 格式(多行,带转义)</h2>
|
||||
<div class="mermaid">
|
||||
<script>document.write("graph LR\n A[\"用户输入\"] --> B[\"数据验证\"]\n B --> C{\"是否有效?\"}\n C -->|是| D[\"保存数据\"]\n C -->|否| E[\"显示错误\"]")</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 测试用例 3: document.write 格式(包含 HTML 实体) -->
|
||||
<div class="test-case">
|
||||
<h2>测试 3: document.write 格式(包含 HTML 实体)</h2>
|
||||
<div class="mermaid">
|
||||
<script>document.write("sequenceDiagram\n Alice->>Bob: Hello Bob!\n Bob-->>Alice: Hi Alice!")</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 测试用例 4: 纯文本格式(无 script 标签) -->
|
||||
<div class="test-case">
|
||||
<h2>测试 4: 纯文本格式(无 script 标签)</h2>
|
||||
<div class="mermaid">
|
||||
flowchart TD
|
||||
Start --> Stop
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 测试用例 5: pre code 格式 -->
|
||||
<div class="test-case">
|
||||
<h2>测试 5: pre code 格式</h2>
|
||||
<pre><code class="language-mermaid">graph TD
|
||||
A --> B
|
||||
B --> C</code></pre>
|
||||
</div>
|
||||
|
||||
<!-- 测试用例 6: 复杂图表(类图) -->
|
||||
<div class="test-case">
|
||||
<h2>测试 6: 复杂图表(类图)</h2>
|
||||
<div class="mermaid">
|
||||
<script>document.write("classDiagram\n class Animal {\n +String name\n +int age\n +makeSound()\n }\n class Dog {\n +String breed\n +bark()\n }\n Animal <|-- Dog")</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
||||
<script>
|
||||
// 模拟 Argon 主题的 Mermaid 初始化逻辑
|
||||
console.log('[测试] 开始初始化 Mermaid');
|
||||
|
||||
// 检测所有 Mermaid 代码块
|
||||
function detectMermaidBlocks() {
|
||||
const blocks = [];
|
||||
const selectors = [
|
||||
'div.mermaid',
|
||||
'pre code.language-mermaid',
|
||||
'pre[data-lang="mermaid"]',
|
||||
'code.mermaid'
|
||||
];
|
||||
|
||||
selectors.forEach(selector => {
|
||||
const elements = document.querySelectorAll(selector);
|
||||
elements.forEach(element => {
|
||||
if (!blocks.includes(element)) {
|
||||
blocks.push(element);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
console.log(`[测试] 检测到 ${blocks.length} 个 Mermaid 代码块`);
|
||||
return blocks;
|
||||
}
|
||||
|
||||
// 提取代码块内容(使用改进后的正则)
|
||||
function extractMermaidCode(element) {
|
||||
let code = '';
|
||||
|
||||
if (element.tagName === 'DIV' && element.classList.contains('mermaid')) {
|
||||
const scriptTag = element.querySelector('script');
|
||||
if (scriptTag) {
|
||||
const scriptContent = scriptTag.textContent || scriptTag.innerText;
|
||||
console.log('[测试] 原始 script 内容:', scriptContent.substring(0, 100));
|
||||
|
||||
// 使用改进后的正则:[\s\S]*? 匹配包括换行在内的所有字符
|
||||
let match = scriptContent.match(/document\.write\s*\(\s*["']([\s\S]*?)["']\s*\)/);
|
||||
|
||||
if (match && match[1]) {
|
||||
code = match[1];
|
||||
console.log('[测试] 从 document.write() 提取到代码,长度:', code.length);
|
||||
} else {
|
||||
// 降级方案:直接提取引号内容
|
||||
match = scriptContent.match(/["']([\s\S]*?)["']/);
|
||||
if (match && match[1]) {
|
||||
code = match[1];
|
||||
console.log('[测试] 从引号内提取到代码,长度:', code.length);
|
||||
} else {
|
||||
const clonedElement = element.cloneNode(true);
|
||||
const scripts = clonedElement.querySelectorAll('script');
|
||||
scripts.forEach(script => script.remove());
|
||||
code = clonedElement.textContent;
|
||||
console.log('[测试] 使用降级方案提取代码');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
code = element.textContent;
|
||||
console.log('[测试] 从纯文本提取代码');
|
||||
}
|
||||
} else if (element.tagName === 'CODE') {
|
||||
code = element.textContent;
|
||||
} else if (element.tagName === 'PRE') {
|
||||
const codeElement = element.querySelector('code');
|
||||
code = codeElement ? codeElement.textContent : element.textContent;
|
||||
}
|
||||
|
||||
// 解码 HTML 实体
|
||||
code = code
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'");
|
||||
|
||||
// 解码转义字符
|
||||
code = code
|
||||
.replace(/\\n/g, '\n')
|
||||
.replace(/\\t/g, '\t')
|
||||
.replace(/\\r/g, '\r')
|
||||
.replace(/\\"/g, '"')
|
||||
.replace(/\\'/g, "'")
|
||||
.replace(/\\\\/g, '\\');
|
||||
|
||||
console.log('[测试] 最终提取的代码:', code.substring(0, 100) + (code.length > 100 ? '...' : ''));
|
||||
return code.trim();
|
||||
}
|
||||
|
||||
// 初始化并渲染
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
theme: 'default',
|
||||
securityLevel: 'loose'
|
||||
});
|
||||
|
||||
const blocks = detectMermaidBlocks();
|
||||
blocks.forEach((block, index) => {
|
||||
const code = extractMermaidCode(block);
|
||||
const chartId = `mermaid-chart-${index}`;
|
||||
|
||||
console.log(`[测试] 渲染图表 ${index}:`, code);
|
||||
|
||||
mermaid.render(chartId, code).then(result => {
|
||||
const container = document.createElement('div');
|
||||
container.innerHTML = result.svg;
|
||||
block.parentNode.replaceChild(container, block);
|
||||
console.log(`[测试] 图表 ${index} 渲染成功`);
|
||||
}).catch(error => {
|
||||
console.error(`[测试] 图表 ${index} 渲染失败:`, error);
|
||||
block.style.border = '2px solid red';
|
||||
block.innerHTML = `<strong style="color: red;">渲染失败: ${error.message}</strong><br><pre>${code}</pre>`;
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user