feat: 支持解析 plantUML
This commit is contained in:
parent
a8c8a72bd6
commit
bb7f6e2772
7
package-lock.json
generated
7
package-lock.json
generated
@ -14,6 +14,7 @@
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
"@vavt/v3-extension": "^3.0.0",
|
||||
"md-editor-v3": "^5.5.0",
|
||||
"plantuml-encoder": "^1.4.0",
|
||||
"scriptjs": "^2.5.9",
|
||||
"view-ui-plus": "^1.3.19",
|
||||
"vue": "^3.5.13"
|
||||
@ -2091,6 +2092,12 @@
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/plantuml-encoder": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/plantuml-encoder/-/plantuml-encoder-1.4.0.tgz",
|
||||
"integrity": "sha512-sxMwpDw/ySY1WB2CE3+IdMuEcWibJ72DDOsXLkSmEaSzwEUaYBT6DWgOfBiHGCux4q433X6+OEFWjlVqp7gL6g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/popper.js": {
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
"@vavt/v3-extension": "^3.0.0",
|
||||
"md-editor-v3": "^5.5.0",
|
||||
"plantuml-encoder": "^1.4.0",
|
||||
"scriptjs": "^2.5.9",
|
||||
"view-ui-plus": "^1.3.19",
|
||||
"vue": "^3.5.13"
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
<template #left>
|
||||
<MdEditor ref="editorRef" v-model="editorState.text" :id="editorState.id" :theme="editorState.theme"
|
||||
:previewTheme="editorState.previewTheme" :style="{ height: editorState.height }"
|
||||
:toolbars="editorState.toolbars" :toolbarsExclude="editorState.toolbarsExclude" @onSave="onSave"
|
||||
@onHtmlChanged="getPreviewedHTML" @onUploadImg="onUploadImg">
|
||||
:toolbars="editorState.toolbars" :toolbarsExclude="editorState.toolbarsExclude" :sanitize="sanitize"
|
||||
@onSave="onSave" @onHtmlChanged="getPreviewedHTML" @onUploadImg="onUploadImg">
|
||||
<template #defToolbars>
|
||||
<NormalToolbar :title="leftSidebarState == 'open' ? '隐藏文件树' : '显示文件树'" @onClick="toggleLeftSideBar">
|
||||
<template #trigger>
|
||||
@ -55,6 +55,7 @@ import { ThemeSwitch, PreviewThemeSwitch, ExportPDF } from '@vavt/v3-extension';
|
||||
import { lineNumbers } from '@codemirror/view';
|
||||
import { ref, reactive, watch, nextTick, onMounted } from "vue";
|
||||
import { Message } from 'view-ui-plus'
|
||||
import { encode as plantumlEncoder } from 'plantuml-encoder';
|
||||
|
||||
// fetchScript 用于将其他 JS 脚本加载进来
|
||||
const fetchScript = (url) => new Promise((resolve) => scriptjs(url, () => resolve()));
|
||||
@ -188,6 +189,54 @@ const toggleCatalog = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const sanitize = (html) => {
|
||||
// 防 XSS 攻击。使用 sanitize-html 处理不安全的 html 内容
|
||||
// !!! 但是会导致目录跳转失败,因为 <h> 标签的部分属性被剔除掉了
|
||||
// html = sanitizeHtml(html);
|
||||
|
||||
// PlantUML 渲染核心逻辑
|
||||
html = html.replace(/@startuml([\s\S]*?)@enduml/g, (_, umlCode) => {
|
||||
const lines = umlCode.split('\n');
|
||||
// 去除换行作用的首尾
|
||||
if (lines[0] == '<br>') {
|
||||
lines.shift();
|
||||
}
|
||||
if (/<p\s+[^>]*>/i.test(lines[lines.length - 1])) {
|
||||
lines.pop();
|
||||
}
|
||||
|
||||
const removeLineBreak = /(.*?)(?=<br>|<\/p>)/i;
|
||||
const extractedLines = lines.map(line => {
|
||||
// 提取类似于 <p data-line="2">text<br> 中的文本
|
||||
line = line.trim().replace(/<\w+\s+[^>]*>(.*?)<[^>]*>/i, '$1');
|
||||
// 去除换行符
|
||||
let match = line.match(removeLineBreak);
|
||||
line = match ? match[1].trim() : line;
|
||||
// 特殊字符串处理,解码 HTML 实体。比如 > 反转义为 >
|
||||
const decodedText = decodeHTMLEntities(line);
|
||||
return decodedText;
|
||||
});
|
||||
extractedLines.unshift('@startuml');
|
||||
extractedLines.push('@enduml');
|
||||
const plantUMLCode = extractedLines.join('\n');
|
||||
console.log(plantUMLCode);
|
||||
|
||||
// 生成 PlantUML 编码字符串
|
||||
const encoded = plantumlEncoder(plantUMLCode);
|
||||
return `<img class="plantuml-diagram"
|
||||
src="https://www.plantuml.com/plantuml/svg/${encoded}"
|
||||
alt="PlantUML Diagram">`;
|
||||
});
|
||||
|
||||
return html;
|
||||
};
|
||||
|
||||
const decodeHTMLEntities = (text) => {
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.innerHTML = text;
|
||||
return textArea.value;
|
||||
};
|
||||
|
||||
const onSave = (v, h) => {
|
||||
// 调用 save 方法保存代码(参数 v 为 markdown code)
|
||||
props.save(v);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user