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` }