From d10172f0171538ff9aa667105b2c48bc05fec804 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Thu, 27 Aug 2026 06:26:45 -0600 Subject: [PATCH] =?UTF-8?q?fix(=E6=B5=81=E7=A8=8B=E5=9B=BE):=20=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E5=90=8D=E9=87=8C=E5=B8=A6=E4=B8=80=E4=B8=AA=E5=8F=8C?= =?UTF-8?q?=E5=BC=95=E5=8F=B7=EF=BC=8C=E7=94=9F=E6=88=90=E7=9A=84=20mermai?= =?UTF-8?q?d=20=E5=B0=B1=E6=98=AF=E5=9D=8F=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 节点和连线的标签是学生自己敲的任意文字,而转换器是直接把它塞进 `"..."` 里: ${nodeId}(("${label}")) 出现一个双引号就把语法撑破了。后果不只是图渲染不出来 —— 这段坏掉的代码会**原样 提交给 AI 打分**,学生完全不知道自己被扣分是因为一个引号。把 `说"你好"` 当节点名 是很自然的写法。 改用 mermaid 的实体转义。`#` 必须先转,否则标签里本来就有的 `#quot;` 之类会被当 成实体解释;换行转成 `
`,不然会截断整条语句。 实测:`说"你好" #1` 现在生成 `说#quot;你好#quot; #35;1`,渲染出来仍然显示 `说"你好" #1`;未转义的那版渲染报语法错误、一个 svg 都出不来。 顺带补上 `edges` 的空值保护 —— `nodes` 有,`edges` 一直没有,拿到 undefined 直接抛。 Co-Authored-By: Claude Opus 5 --- .../composables/useMermaidConverter.ts | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/apps/web/src/oj/problem/composables/useMermaidConverter.ts b/apps/web/src/oj/problem/composables/useMermaidConverter.ts index 0cd2eb2..6faec35 100644 --- a/apps/web/src/oj/problem/composables/useMermaidConverter.ts +++ b/apps/web/src/oj/problem/composables/useMermaidConverter.ts @@ -1,9 +1,26 @@ /** - * 将流程图JSON数据转换为Mermaid格式 + * 节点/连线标签是学生自己敲的任意文字,而下面是直接把它塞进 "..." 里。 + * 出现一个双引号就会把 mermaid 语法撑破:图渲染不出来,坏掉的代码还会原样送去 + * 给 AI 打分,学生完全不知道自己被扣分是因为一个引号。 + * + * mermaid 用 `#NN;` 形式的实体转义。`#` 必须**先**转,否则标签里本来就有的 + * `#quot;` 之类会被当成实体解释。换行转成
,不然会截断整条语句。 + */ +function escapeLabel(raw: unknown) { + return String(raw ?? "") + .replace(/#/g, "#35;") + .replace(/"/g, "#quot;") + .replace(/\r?\n/g, "
") +} + +/** + * 将流程图 JSON 数据转换为 Mermaid 格式 */ export function useMermaidConverter() { const convertToMermaid = (flowchartData: any) => { - const { nodes, edges } = flowchartData + const nodes = flowchartData?.nodes + // edges 原来没做空值保护(nodes 有),拿到 undefined 会直接抛 + const edges = flowchartData?.edges ?? [] if (!nodes || nodes.length === 0) { return "graph TD\n A[空流程图]" @@ -22,7 +39,9 @@ export function useMermaidConverter() { // 处理节点 - 根据原始类型和自定义标签 nodes.forEach((node: any) => { const nodeId = safeId(node.id) - const label = node.data?.customLabel || node.data?.label || "节点" + const label = escapeLabel( + node.data?.customLabel || node.data?.label || "节点", + ) const originalType = node.data?.originalType || node.type // 根据节点原始类型确定Mermaid语法 @@ -60,10 +79,10 @@ export function useMermaidConverter() { edges.forEach((edge: any) => { const source = safeId(edge.source) const target = safeId(edge.target) - const label = edge.label ?? "" + const rawLabel = String(edge.label ?? "") - if (label && label.trim() !== "") { - mermaid += ` ${source} -->|"${label}"| ${target}\n` + if (rawLabel.trim() !== "") { + mermaid += ` ${source} -->|"${escapeLabel(rawLabel)}"| ${target}\n` } else { mermaid += ` ${source} --> ${target}\n` }