fix mermaid
This commit is contained in:
201
package-lock.json
generated
201
package-lock.json
generated
@@ -50,6 +50,7 @@
|
|||||||
"@rsbuild/plugin-vue": "^1.2.7",
|
"@rsbuild/plugin-vue": "^1.2.7",
|
||||||
"@types/canvas-confetti": "^1.9.0",
|
"@types/canvas-confetti": "^1.9.0",
|
||||||
"@types/node": "^25.6.0",
|
"@types/node": "^25.6.0",
|
||||||
|
"@vue/tsconfig": "^0.9.1",
|
||||||
"prettier": "^3.8.3",
|
"prettier": "^3.8.3",
|
||||||
"typescript": "^6.0.3",
|
"typescript": "^6.0.3",
|
||||||
"unplugin-auto-import": "^21.0.0",
|
"unplugin-auto-import": "^21.0.0",
|
||||||
@@ -959,25 +960,6 @@
|
|||||||
"@module-federation/sdk": "0.22.0"
|
"@module-federation/sdk": "0.22.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@napi-rs/wasm-runtime": {
|
|
||||||
"version": "1.1.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
|
|
||||||
"integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@tybys/wasm-util": "^0.10.1"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@emnapi/core": "^1.7.1",
|
|
||||||
"@emnapi/runtime": "^1.7.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@rsbuild/core": {
|
"node_modules/@rsbuild/core": {
|
||||||
"version": "1.7.5",
|
"version": "1.7.5",
|
||||||
"resolved": "https://registry.npmjs.org/@rsbuild/core/-/core-1.7.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rsbuild/core/-/core-1.7.5.tgz",
|
||||||
@@ -1211,168 +1193,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rspack/binding": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-0kZPplW9GWx8mfC6DfsaRY3QBIYPuUs42JfmSM6aSb8tMHZAXQeLeMB8M+h8i4SeI+aFtCgO6UuYGtyWf7+L+A==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"optionalDependencies": {
|
|
||||||
"@rspack/binding-darwin-arm64": "2.0.2",
|
|
||||||
"@rspack/binding-darwin-x64": "2.0.2",
|
|
||||||
"@rspack/binding-linux-arm64-gnu": "2.0.2",
|
|
||||||
"@rspack/binding-linux-arm64-musl": "2.0.2",
|
|
||||||
"@rspack/binding-linux-x64-gnu": "2.0.2",
|
|
||||||
"@rspack/binding-linux-x64-musl": "2.0.2",
|
|
||||||
"@rspack/binding-wasm32-wasi": "2.0.2",
|
|
||||||
"@rspack/binding-win32-arm64-msvc": "2.0.2",
|
|
||||||
"@rspack/binding-win32-ia32-msvc": "2.0.2",
|
|
||||||
"@rspack/binding-win32-x64-msvc": "2.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@rspack/binding-darwin-arm64": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-0o7lbgBBsDlICWdjIH0q3e0BsSco4GRiImHWVfZSVEG+q2+ykZJvSvYCVhPM1Co375Z0S3VMPa/8SjcY1FHwlw==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rspack/binding-darwin-x64": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-tOwxZpoPlTlRs/w6UyUinXJ4TYRVHMlR7+eQxO1R3muKpixvhXQjtvoaY16HuFyTVky5F0IfOoWr3x9FEsgdLg==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rspack/binding-linux-arm64-gnu": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-1ZD4YFhG1rmgqj+W8hfwHyKV8xDxGsc/3KgU0FwmiVEX7JfzhCkgBO/xlCG79kRKSrzuVzt4icO/G3cCKn0pag==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rspack/binding-linux-arm64-musl": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-/PtTkM/DsDLjeuXTmeJeRfbjCDbcL9jvoVgZrgxYFZ28y2cdLvbChbW9uigOzs5dQEs1CIBQXMTTj7KhdBTuQg==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rspack/binding-linux-x64-gnu": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-bBjsZxMHRaPo6X9SokApm6ucs+UhXtAJFyJJyuk2BH4XJsLeCU9Dz1vMwioeohFbJUUeTASVPm6/BL+RhSaunw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rspack/binding-linux-x64-musl": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-HjlpInqzabDNkhVsUJpsHPqa9QYVWBViJoyWNjzXCAW0vKMDvwaphyUvokSinX8FGTlZi/sr5UEaHJo6XtQ35g==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rspack/binding-wasm32-wasi": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-YaRYNFLJRpkGfYjSWR7n9f+nQKtrlmrrffpAn/blc2geHcRvXoBc5SCs1idPtsLhj7H9qWWhs7ucjyHy4csWFg==",
|
|
||||||
"cpu": [
|
|
||||||
"wasm32"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@emnapi/core": "1.10.0",
|
|
||||||
"@emnapi/runtime": "1.10.0",
|
|
||||||
"@napi-rs/wasm-runtime": "1.1.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@rspack/binding-win32-arm64-msvc": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-d/3kTEKq+asLjRFPO96t+wfWiM7DLN76VQEPDD9bc1kdsZXlVJBuvyXfsgK8bbEvKplWXYcSsokhmEnuXrLOpg==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rspack/binding-win32-ia32-msvc": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-161cWineq3RW+Jdm1FAfSpXeUtYWvhB3kAbm46vNT9h/YYz+spwsFMvveAZ1nsVSVL0IC5lDBGUte7yUAY8K2g==",
|
|
||||||
"cpu": [
|
|
||||||
"ia32"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rspack/binding-win32-x64-msvc": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-y7Q0S1FE+OlkL5GMqLG0PwxrPw6E1r892KhGrGKE1Vdufe5YTEx6xTPxzZ+b7N2KPD7s9G1/iJmWHQxb1+Bjkg==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rspack/lite-tapable": {
|
"node_modules/@rspack/lite-tapable": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rspack/lite-tapable/-/lite-tapable-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rspack/lite-tapable/-/lite-tapable-1.1.0.tgz",
|
||||||
@@ -2215,6 +2035,25 @@
|
|||||||
"integrity": "sha512-24uqU4OIiX29ryC3MeWid/Xf2fa2EFRUVLb77nRhk+UrTVrh/XiGtFAFmJBAtBRbjwNdsPRP+jj/OL27Eg1NDA==",
|
"integrity": "sha512-24uqU4OIiX29ryC3MeWid/Xf2fa2EFRUVLb77nRhk+UrTVrh/XiGtFAFmJBAtBRbjwNdsPRP+jj/OL27Eg1NDA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@vue/tsconfig": {
|
||||||
|
"version": "0.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.9.1.tgz",
|
||||||
|
"integrity": "sha512-buvjm+9NzLCJL29KY1j1991YYJ5e6275OiK+G4jtmfIb+z4POywbdm0wXusT9adVWqe0xqg70TbI7+mRx4uU9w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">= 5.8",
|
||||||
|
"vue": "^3.4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"vue": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@vueuse/core": {
|
"node_modules/@vueuse/core": {
|
||||||
"version": "14.3.0",
|
"version": "14.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-14.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-14.3.0.tgz",
|
||||||
|
|||||||
@@ -52,6 +52,7 @@
|
|||||||
"@rsbuild/plugin-vue": "^1.2.7",
|
"@rsbuild/plugin-vue": "^1.2.7",
|
||||||
"@types/canvas-confetti": "^1.9.0",
|
"@types/canvas-confetti": "^1.9.0",
|
||||||
"@types/node": "^25.6.0",
|
"@types/node": "^25.6.0",
|
||||||
|
"@vue/tsconfig": "^0.9.1",
|
||||||
"prettier": "^3.8.3",
|
"prettier": "^3.8.3",
|
||||||
"typescript": "^6.0.3",
|
"typescript": "^6.0.3",
|
||||||
"unplugin-auto-import": "^21.0.0",
|
"unplugin-auto-import": "^21.0.0",
|
||||||
|
|||||||
@@ -6,20 +6,18 @@ const problemStore = useProblemStore()
|
|||||||
const { problem } = storeToRefs(problemStore)
|
const { problem } = storeToRefs(problemStore)
|
||||||
const mermaidContainer = useTemplateRef<HTMLElement>("mermaidContainer")
|
const mermaidContainer = useTemplateRef<HTMLElement>("mermaidContainer")
|
||||||
|
|
||||||
// 使用 mermaid composable
|
|
||||||
const { renderError, renderFlowchart } = useMermaid()
|
const { renderError, renderFlowchart } = useMermaid()
|
||||||
|
|
||||||
// 渲染流程图的函数
|
|
||||||
const renderProblemFlowchart = async () => {
|
const renderProblemFlowchart = async () => {
|
||||||
if (problem.value?.mermaid_code) {
|
await renderFlowchart(
|
||||||
await renderFlowchart(mermaidContainer.value, problem.value.mermaid_code)
|
mermaidContainer.value,
|
||||||
}
|
problem.value?.mermaid_code ?? "",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化Mermaid并渲染
|
onMounted(renderProblemFlowchart)
|
||||||
onMounted(() => {
|
|
||||||
renderProblemFlowchart()
|
watch(() => problem.value?.mermaid_code, renderProblemFlowchart)
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,103 +1,35 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { copyToClipboard, getRandomId } from "utils/functions"
|
import { copyToClipboard } from "utils/functions"
|
||||||
|
import { useMermaid } from "shared/composables/useMermaid"
|
||||||
// 动态导入 mermaid
|
|
||||||
let mermaid: any = null
|
|
||||||
|
|
||||||
const modelValue = defineModel<string>({ default: "" })
|
const modelValue = defineModel<string>({ default: "" })
|
||||||
const mermaidContainer = useTemplateRef<HTMLElement>("mermaidContainer")
|
const mermaidContainer = useTemplateRef<HTMLElement>("mermaidContainer")
|
||||||
|
|
||||||
// 渲染状态
|
const { renderFlowchart, renderError, renderSuccess } = useMermaid()
|
||||||
const renderSuccess = ref(false)
|
|
||||||
|
|
||||||
// 定义事件
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
renderSuccess: []
|
renderSuccess: []
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
// 动态加载 Mermaid
|
|
||||||
const loadMermaid = async () => {
|
|
||||||
if (!mermaid) {
|
|
||||||
const mermaidModule = await import("mermaid")
|
|
||||||
mermaid = mermaidModule.default
|
|
||||||
mermaid.initialize({
|
|
||||||
startOnLoad: false,
|
|
||||||
securityLevel: "strict",
|
|
||||||
theme: "default",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return mermaid
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化
|
|
||||||
onMounted(async () => {
|
|
||||||
await loadMermaid()
|
|
||||||
nextTick(() => {
|
|
||||||
renderMermaid()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// 监听代码变化
|
|
||||||
watch(modelValue, () => {
|
|
||||||
renderMermaid()
|
|
||||||
})
|
|
||||||
|
|
||||||
// 渲染Mermaid图表
|
|
||||||
const renderMermaid = async () => {
|
const renderMermaid = async () => {
|
||||||
if (!mermaidContainer.value) {
|
await renderFlowchart(mermaidContainer.value, modelValue.value)
|
||||||
renderSuccess.value = false
|
if (renderSuccess.value) emit("renderSuccess")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 总是先清空容器
|
onMounted(() => {
|
||||||
mermaidContainer.value.innerHTML = ""
|
nextTick(renderMermaid)
|
||||||
|
})
|
||||||
|
|
||||||
// 如果没有内容,直接返回
|
watch(modelValue, renderMermaid)
|
||||||
if (!modelValue.value.trim()) {
|
|
||||||
renderSuccess.value = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 确保 mermaid 已加载
|
|
||||||
const mermaidInstance = await loadMermaid()
|
|
||||||
const id = `mermaid-${getRandomId()}`
|
|
||||||
const { svg } = await mermaidInstance.render(id, modelValue.value)
|
|
||||||
mermaidContainer.value.innerHTML = svg
|
|
||||||
|
|
||||||
// 渲染成功
|
|
||||||
renderSuccess.value = true
|
|
||||||
emit("renderSuccess")
|
|
||||||
} catch (error: any) {
|
|
||||||
const errorMessage = error?.message || "请检查代码语法"
|
|
||||||
renderSuccess.value = false
|
|
||||||
|
|
||||||
const errorDiv = document.createElement("div")
|
|
||||||
errorDiv.style.cssText =
|
|
||||||
"color: #ff4d4f; padding: 20px; text-align: center; border: 1px dashed #ff4d4f; border-radius: 4px;"
|
|
||||||
const titleP = document.createElement("p")
|
|
||||||
titleP.textContent = "Mermaid语法错误"
|
|
||||||
const detailP = document.createElement("p")
|
|
||||||
detailP.style.cssText = "font-size: 12px; color: #666;"
|
|
||||||
detailP.textContent = errorMessage
|
|
||||||
errorDiv.appendChild(titleP)
|
|
||||||
errorDiv.appendChild(detailP)
|
|
||||||
mermaidContainer.value.innerHTML = ""
|
|
||||||
mermaidContainer.value.appendChild(errorDiv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清空代码
|
|
||||||
const clearCode = () => {
|
const clearCode = () => {
|
||||||
modelValue.value = ""
|
modelValue.value = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// 复制代码
|
const copyCode = () => {
|
||||||
const copyCode = async () => {
|
|
||||||
copyToClipboard(modelValue.value)
|
copyToClipboard(modelValue.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 组件卸载时清空容器
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
if (mermaidContainer.value) {
|
if (mermaidContainer.value) {
|
||||||
mermaidContainer.value.innerHTML = ""
|
mermaidContainer.value.innerHTML = ""
|
||||||
@@ -121,7 +53,6 @@ onBeforeUnmount(() => {
|
|||||||
</n-flex>
|
</n-flex>
|
||||||
<n-input
|
<n-input
|
||||||
class="code-editor"
|
class="code-editor"
|
||||||
ref="codeEditor"
|
|
||||||
v-model:value="modelValue"
|
v-model:value="modelValue"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
:autosize="{ minRows: 10, maxRows: 20 }"
|
:autosize="{ minRows: 10, maxRows: 20 }"
|
||||||
@@ -134,6 +65,14 @@ onBeforeUnmount(() => {
|
|||||||
✓ 渲染成功
|
✓ 渲染成功
|
||||||
</n-tag>
|
</n-tag>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
|
<n-alert
|
||||||
|
v-if="renderError"
|
||||||
|
type="error"
|
||||||
|
title="Mermaid 语法错误"
|
||||||
|
style="margin-bottom: 8px"
|
||||||
|
>
|
||||||
|
<n-text style="font-size: 12px">{{ renderError }}</n-text>
|
||||||
|
</n-alert>
|
||||||
<div ref="mermaidContainer" class="mermaid-container"></div>
|
<div ref="mermaidContainer" class="mermaid-container"></div>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
|
|||||||
@@ -1,205 +1,118 @@
|
|||||||
import { getRandomId } from "utils/functions"
|
import { getRandomId } from "utils/functions"
|
||||||
|
|
||||||
const mermaidThemeVariables = {
|
const mermaidThemeVariables = {
|
||||||
primaryColor: "#e0f2fe",
|
primaryColor: "#eff6ff",
|
||||||
primaryTextColor: "#0f172a",
|
primaryTextColor: "#1d4ed8",
|
||||||
primaryBorderColor: "#0284c7",
|
primaryBorderColor: "#3b82f6",
|
||||||
lineColor: "#64748b",
|
lineColor: "#94a3b8",
|
||||||
secondaryColor: "#f5f3ff",
|
|
||||||
tertiaryColor: "#ecfdf5",
|
|
||||||
background: "#ffffff",
|
background: "#ffffff",
|
||||||
mainBkg: "#f8fafc",
|
mainBkg: "#eff6ff",
|
||||||
secondBkg: "#eef2ff",
|
|
||||||
tertiaryBkg: "#f0fdfa",
|
|
||||||
nodeBorder: "#2563eb",
|
|
||||||
clusterBkg: "#f8fafc",
|
|
||||||
clusterBorder: "#cbd5e1",
|
|
||||||
edgeLabelBackground: "#ffffff",
|
|
||||||
fontFamily:
|
fontFamily:
|
||||||
'Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
'Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
||||||
}
|
}
|
||||||
|
|
||||||
const semanticNodeClasses = [
|
|
||||||
"startNode",
|
|
||||||
"endNode",
|
|
||||||
"startEnd",
|
|
||||||
"input",
|
|
||||||
"output",
|
|
||||||
"process",
|
|
||||||
"decision",
|
|
||||||
"loop",
|
|
||||||
]
|
|
||||||
|
|
||||||
const displayStyleId = "oj-mermaid-display-style"
|
const displayStyleId = "oj-mermaid-display-style"
|
||||||
|
|
||||||
|
const shapes = ["rect", "polygon", "ellipse", "circle", "path"]
|
||||||
|
|
||||||
|
function nodeShapeRule(cls: string, fill: string, stroke: string) {
|
||||||
|
const sel = shapes
|
||||||
|
.map((s) => `.oj-mermaid-flowchart g.node.${cls} ${s}`)
|
||||||
|
.join(",\n")
|
||||||
|
return `${sel} { fill: ${fill} !important; stroke: ${stroke} !important; }`
|
||||||
|
}
|
||||||
|
|
||||||
|
function nodeLabelRule(cls: string, color: string) {
|
||||||
|
const bases = [".label", ".nodeLabel", ".nodeLabel p", ".label span"]
|
||||||
|
const sel = bases
|
||||||
|
.map((b) => `.oj-mermaid-flowchart g.node.${cls} ${b}`)
|
||||||
|
.join(",\n")
|
||||||
|
return `${sel} { color: ${color} !important; fill: ${color} !important; }`
|
||||||
|
}
|
||||||
|
|
||||||
const mermaidDisplayStyle = `
|
const mermaidDisplayStyle = `
|
||||||
.oj-mermaid-flowchart {
|
.oj-mermaid-flowchart {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Default node */
|
||||||
.oj-mermaid-flowchart g.node rect,
|
.oj-mermaid-flowchart g.node rect,
|
||||||
.oj-mermaid-flowchart g.node polygon,
|
.oj-mermaid-flowchart g.node polygon,
|
||||||
.oj-mermaid-flowchart g.node ellipse,
|
.oj-mermaid-flowchart g.node ellipse,
|
||||||
.oj-mermaid-flowchart g.node circle,
|
.oj-mermaid-flowchart g.node circle,
|
||||||
.oj-mermaid-flowchart g.node path {
|
.oj-mermaid-flowchart g.node path {
|
||||||
stroke-width: 2.5px !important;
|
fill: #eff6ff !important;
|
||||||
filter: drop-shadow(0 6px 12px rgba(15, 23, 42, 0.12));
|
stroke: #3b82f6 !important;
|
||||||
}
|
stroke-width: 1.8px !important;
|
||||||
|
|
||||||
.oj-mermaid-flowchart g.node.startNode rect,
|
|
||||||
.oj-mermaid-flowchart g.node.startNode polygon,
|
|
||||||
.oj-mermaid-flowchart g.node.startNode ellipse,
|
|
||||||
.oj-mermaid-flowchart g.node.startNode circle,
|
|
||||||
.oj-mermaid-flowchart g.node.startNode path,
|
|
||||||
.oj-mermaid-flowchart g.node.startEnd rect,
|
|
||||||
.oj-mermaid-flowchart g.node.startEnd polygon,
|
|
||||||
.oj-mermaid-flowchart g.node.startEnd ellipse,
|
|
||||||
.oj-mermaid-flowchart g.node.startEnd circle,
|
|
||||||
.oj-mermaid-flowchart g.node.startEnd path {
|
|
||||||
fill: #dcfce7 !important;
|
|
||||||
stroke: #16a34a !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.oj-mermaid-flowchart g.node.endNode rect,
|
|
||||||
.oj-mermaid-flowchart g.node.endNode polygon,
|
|
||||||
.oj-mermaid-flowchart g.node.endNode ellipse,
|
|
||||||
.oj-mermaid-flowchart g.node.endNode circle,
|
|
||||||
.oj-mermaid-flowchart g.node.endNode path {
|
|
||||||
fill: #fee2e2 !important;
|
|
||||||
stroke: #dc2626 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.oj-mermaid-flowchart g.node.input rect,
|
|
||||||
.oj-mermaid-flowchart g.node.input polygon,
|
|
||||||
.oj-mermaid-flowchart g.node.input ellipse,
|
|
||||||
.oj-mermaid-flowchart g.node.input circle,
|
|
||||||
.oj-mermaid-flowchart g.node.input path {
|
|
||||||
fill: #dbeafe !important;
|
|
||||||
stroke: #2563eb !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.oj-mermaid-flowchart g.node.output rect,
|
|
||||||
.oj-mermaid-flowchart g.node.output polygon,
|
|
||||||
.oj-mermaid-flowchart g.node.output ellipse,
|
|
||||||
.oj-mermaid-flowchart g.node.output circle,
|
|
||||||
.oj-mermaid-flowchart g.node.output path {
|
|
||||||
fill: #ede9fe !important;
|
|
||||||
stroke: #7c3aed !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.oj-mermaid-flowchart g.node.process rect,
|
|
||||||
.oj-mermaid-flowchart g.node.process polygon,
|
|
||||||
.oj-mermaid-flowchart g.node.process ellipse,
|
|
||||||
.oj-mermaid-flowchart g.node.process circle,
|
|
||||||
.oj-mermaid-flowchart g.node.process path {
|
|
||||||
fill: #f0f9ff !important;
|
|
||||||
stroke: #0284c7 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.oj-mermaid-flowchart g.node.decision rect,
|
|
||||||
.oj-mermaid-flowchart g.node.decision polygon,
|
|
||||||
.oj-mermaid-flowchart g.node.decision ellipse,
|
|
||||||
.oj-mermaid-flowchart g.node.decision circle,
|
|
||||||
.oj-mermaid-flowchart g.node.decision path {
|
|
||||||
fill: #fef3c7 !important;
|
|
||||||
stroke: #d97706 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.oj-mermaid-flowchart g.node.loop rect,
|
|
||||||
.oj-mermaid-flowchart g.node.loop polygon,
|
|
||||||
.oj-mermaid-flowchart g.node.loop ellipse,
|
|
||||||
.oj-mermaid-flowchart g.node.loop circle,
|
|
||||||
.oj-mermaid-flowchart g.node.loop path {
|
|
||||||
fill: #fae8ff !important;
|
|
||||||
stroke: #c026d3 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-0 rect,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-0 polygon,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-0 ellipse,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-0 circle,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-0 path {
|
|
||||||
fill: #dbeafe !important;
|
|
||||||
stroke: #2563eb !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-1 rect,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-1 polygon,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-1 ellipse,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-1 circle,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-1 path {
|
|
||||||
fill: #ccfbf1 !important;
|
|
||||||
stroke: #0d9488 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-2 rect,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-2 polygon,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-2 ellipse,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-2 circle,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-2 path {
|
|
||||||
fill: #ede9fe !important;
|
|
||||||
stroke: #7c3aed !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-3 rect,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-3 polygon,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-3 ellipse,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-3 circle,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-3 path {
|
|
||||||
fill: #ffe4e6 !important;
|
|
||||||
stroke: #e11d48 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-4 rect,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-4 polygon,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-4 ellipse,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-4 circle,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-4 path {
|
|
||||||
fill: #fef3c7 !important;
|
|
||||||
stroke: #d97706 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-5 rect,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-5 polygon,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-5 ellipse,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-5 circle,
|
|
||||||
.oj-mermaid-flowchart g.node.oj-node-palette-5 path {
|
|
||||||
fill: #dcfce7 !important;
|
|
||||||
stroke: #16a34a !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Default node text */
|
||||||
.oj-mermaid-flowchart g.node .label,
|
.oj-mermaid-flowchart g.node .label,
|
||||||
.oj-mermaid-flowchart g.node .nodeLabel,
|
.oj-mermaid-flowchart g.node .nodeLabel,
|
||||||
.oj-mermaid-flowchart g.node .nodeLabel p,
|
.oj-mermaid-flowchart g.node .nodeLabel p,
|
||||||
.oj-mermaid-flowchart g.node .label span {
|
.oj-mermaid-flowchart g.node .label span {
|
||||||
color: #0f172a !important;
|
color: #1d4ed8 !important;
|
||||||
fill: #0f172a !important;
|
fill: #1d4ed8 !important;
|
||||||
font-weight: 650 !important;
|
font-weight: 600 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* startNode / startEnd */
|
||||||
|
${nodeShapeRule("startNode", "#dcfce7", "#16a34a")}
|
||||||
|
${nodeShapeRule("startEnd", "#dcfce7", "#16a34a")}
|
||||||
|
${nodeLabelRule("startNode", "#166534")}
|
||||||
|
${nodeLabelRule("startEnd", "#166534")}
|
||||||
|
|
||||||
|
/* endNode */
|
||||||
|
${nodeShapeRule("endNode", "#fee2e2", "#dc2626")}
|
||||||
|
${nodeLabelRule("endNode", "#991b1b")}
|
||||||
|
|
||||||
|
/* input */
|
||||||
|
${nodeShapeRule("input", "#dbeafe", "#2563eb")}
|
||||||
|
${nodeLabelRule("input", "#1e40af")}
|
||||||
|
|
||||||
|
/* output */
|
||||||
|
${nodeShapeRule("output", "#ede9fe", "#7c3aed")}
|
||||||
|
${nodeLabelRule("output", "#5b21b6")}
|
||||||
|
|
||||||
|
/* process */
|
||||||
|
${nodeShapeRule("process", "#f1f5f9", "#64748b")}
|
||||||
|
${nodeLabelRule("process", "#334155")}
|
||||||
|
|
||||||
|
/* decision */
|
||||||
|
${nodeShapeRule("decision", "#fef9c3", "#ca8a04")}
|
||||||
|
${nodeLabelRule("decision", "#92400e")}
|
||||||
|
|
||||||
|
/* loop */
|
||||||
|
${nodeShapeRule("loop", "#fae8ff", "#c026d3")}
|
||||||
|
${nodeLabelRule("loop", "#7e22ce")}
|
||||||
|
|
||||||
|
/* Edges */
|
||||||
.oj-mermaid-flowchart .edgePaths path.path,
|
.oj-mermaid-flowchart .edgePaths path.path,
|
||||||
.oj-mermaid-flowchart .flowchart-link {
|
.oj-mermaid-flowchart .flowchart-link {
|
||||||
stroke: #64748b !important;
|
stroke: #94a3b8 !important;
|
||||||
stroke-width: 2.4px !important;
|
stroke-width: 1.8px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Arrowheads */
|
||||||
.oj-mermaid-flowchart marker path,
|
.oj-mermaid-flowchart marker path,
|
||||||
.oj-mermaid-flowchart .marker {
|
.oj-mermaid-flowchart .marker {
|
||||||
fill: #64748b !important;
|
fill: #94a3b8 !important;
|
||||||
stroke: #64748b !important;
|
stroke: #94a3b8 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Edge label background */
|
||||||
.oj-mermaid-flowchart .edgeLabel rect,
|
.oj-mermaid-flowchart .edgeLabel rect,
|
||||||
.oj-mermaid-flowchart .edgeLabel .labelBkg {
|
.oj-mermaid-flowchart .edgeLabel .labelBkg {
|
||||||
fill: rgba(255, 255, 255, 0.94) !important;
|
fill: rgba(255, 255, 255, 0.9) !important;
|
||||||
stroke: #cbd5e1 !important;
|
stroke: #e2e8f0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Edge label text */
|
||||||
.oj-mermaid-flowchart .edgeLabel,
|
.oj-mermaid-flowchart .edgeLabel,
|
||||||
.oj-mermaid-flowchart .edgeLabel span,
|
.oj-mermaid-flowchart .edgeLabel span,
|
||||||
.oj-mermaid-flowchart .edgeLabel p {
|
.oj-mermaid-flowchart .edgeLabel p {
|
||||||
color: #334155 !important;
|
color: #475569 !important;
|
||||||
font-weight: 600 !important;
|
font-weight: 600 !important;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
@@ -214,16 +127,6 @@ function applyFlowchartDisplayStyle(container: HTMLElement) {
|
|||||||
|
|
||||||
svg.classList.add("oj-mermaid-flowchart")
|
svg.classList.add("oj-mermaid-flowchart")
|
||||||
|
|
||||||
const nodes = Array.from(svg.querySelectorAll<SVGGElement>("g.node"))
|
|
||||||
nodes.forEach((node, index) => {
|
|
||||||
const hasSemanticClass = semanticNodeClasses.some((className) =>
|
|
||||||
node.classList.contains(className),
|
|
||||||
)
|
|
||||||
if (!hasSemanticClass) {
|
|
||||||
node.classList.add(`oj-node-palette-${index % 6}`)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
svg.querySelector(`#${displayStyleId}`)?.remove()
|
svg.querySelector(`#${displayStyleId}`)?.remove()
|
||||||
const style = document.createElementNS(svgNamespace, "style")
|
const style = document.createElementNS(svgNamespace, "style")
|
||||||
style.setAttribute("id", displayStyleId)
|
style.setAttribute("id", displayStyleId)
|
||||||
@@ -231,46 +134,44 @@ function applyFlowchartDisplayStyle(container: HTMLElement) {
|
|||||||
svg.insertBefore(style, svg.firstChild)
|
svg.insertBefore(style, svg.firstChild)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useMermaid() {
|
let mermaidInstance: any = null
|
||||||
// 渲染状态
|
|
||||||
const renderError = ref<string | null>(null)
|
|
||||||
|
|
||||||
// 动态导入 mermaid
|
async function loadMermaid() {
|
||||||
let mermaid: any = null
|
if (!mermaidInstance) {
|
||||||
|
|
||||||
// 动态加载 Mermaid
|
|
||||||
const loadMermaid = async () => {
|
|
||||||
if (!mermaid) {
|
|
||||||
const mermaidModule = await import("mermaid")
|
const mermaidModule = await import("mermaid")
|
||||||
mermaid = mermaidModule.default
|
mermaidInstance = mermaidModule.default
|
||||||
mermaid.initialize({
|
mermaidInstance.initialize({
|
||||||
startOnLoad: false,
|
startOnLoad: false,
|
||||||
securityLevel: "strict",
|
securityLevel: "strict",
|
||||||
theme: "base",
|
theme: "base",
|
||||||
themeVariables: mermaidThemeVariables,
|
themeVariables: mermaidThemeVariables,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return mermaid
|
return mermaidInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
// 渲染流程图的函数
|
export function useMermaid() {
|
||||||
|
const renderError = ref<string | null>(null)
|
||||||
|
const renderSuccess = ref(false)
|
||||||
|
|
||||||
const renderFlowchart = async (
|
const renderFlowchart = async (
|
||||||
container: HTMLElement | null,
|
container: HTMLElement | null,
|
||||||
mermaidCode: string,
|
mermaidCode: string,
|
||||||
) => {
|
) => {
|
||||||
try {
|
|
||||||
renderError.value = null
|
renderError.value = null
|
||||||
|
renderSuccess.value = false
|
||||||
|
|
||||||
// 确保 mermaid 已加载
|
if (container) container.innerHTML = ""
|
||||||
await loadMermaid()
|
|
||||||
|
|
||||||
// 渲染流程图
|
if (!container || !mermaidCode?.trim()) return
|
||||||
if (container && mermaidCode) {
|
|
||||||
|
try {
|
||||||
|
const m = await loadMermaid()
|
||||||
const id = `mermaid-${getRandomId()}`
|
const id = `mermaid-${getRandomId()}`
|
||||||
const { svg } = await mermaid.render(id, mermaidCode)
|
const { svg } = await m.render(id, mermaidCode)
|
||||||
container.innerHTML = svg
|
container.innerHTML = svg
|
||||||
applyFlowchartDisplayStyle(container)
|
applyFlowchartDisplayStyle(container)
|
||||||
}
|
renderSuccess.value = true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
renderError.value =
|
renderError.value =
|
||||||
error instanceof Error
|
error instanceof Error
|
||||||
@@ -279,13 +180,13 @@ export function useMermaid() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清除渲染错误
|
|
||||||
const clearError = () => {
|
const clearError = () => {
|
||||||
renderError.value = null
|
renderError.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
renderError: readonly(renderError),
|
renderError: readonly(renderError),
|
||||||
|
renderSuccess: readonly(renderSuccess),
|
||||||
renderFlowchart,
|
renderFlowchart,
|
||||||
clearError,
|
clearError,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,12 @@
|
|||||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
|
"paths": {
|
||||||
|
"utils/*": ["./src/utils/*"],
|
||||||
|
"oj/*": ["./src/oj/*"],
|
||||||
|
"admin/*": ["./src/admin/*"],
|
||||||
|
"shared/*": ["./src/shared/*"]
|
||||||
|
},
|
||||||
|
|
||||||
/* Linting */
|
/* Linting */
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
|||||||
Reference in New Issue
Block a user