fix(流程图): 节点名里带一个双引号,生成的 mermaid 就是坏的
节点和连线的标签是学生自己敲的任意文字,而转换器是直接把它塞进 `"..."` 里:
${nodeId}(("${label}"))
出现一个双引号就把语法撑破了。后果不只是图渲染不出来 —— 这段坏掉的代码会**原样
提交给 AI 打分**,学生完全不知道自己被扣分是因为一个引号。把 `说"你好"` 当节点名
是很自然的写法。
改用 mermaid 的实体转义。`#` 必须先转,否则标签里本来就有的 `#quot;` 之类会被当
成实体解释;换行转成 `<br/>`,不然会截断整条语句。
实测:`说"你好" #1` 现在生成 `说#quot;你好#quot; #35;1`,渲染出来仍然显示
`说"你好" #1`;未转义的那版渲染报语法错误、一个 svg 都出不来。
顺带补上 `edges` 的空值保护 —— `nodes` 有,`edges` 一直没有,拿到 undefined 直接抛。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* 节点/连线标签是学生自己敲的任意文字,而下面是直接把它塞进 "..." 里。
|
||||||
|
* 出现一个双引号就会把 mermaid 语法撑破:图渲染不出来,坏掉的代码还会原样送去
|
||||||
|
* 给 AI 打分,学生完全不知道自己被扣分是因为一个引号。
|
||||||
|
*
|
||||||
|
* mermaid 用 `#NN;` 形式的实体转义。`#` 必须**先**转,否则标签里本来就有的
|
||||||
|
* `#quot;` 之类会被当成实体解释。换行转成 <br/>,不然会截断整条语句。
|
||||||
|
*/
|
||||||
|
function escapeLabel(raw: unknown) {
|
||||||
|
return String(raw ?? "")
|
||||||
|
.replace(/#/g, "#35;")
|
||||||
|
.replace(/"/g, "#quot;")
|
||||||
|
.replace(/\r?\n/g, "<br/>")
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将流程图 JSON 数据转换为 Mermaid 格式
|
* 将流程图 JSON 数据转换为 Mermaid 格式
|
||||||
*/
|
*/
|
||||||
export function useMermaidConverter() {
|
export function useMermaidConverter() {
|
||||||
const convertToMermaid = (flowchartData: any) => {
|
const convertToMermaid = (flowchartData: any) => {
|
||||||
const { nodes, edges } = flowchartData
|
const nodes = flowchartData?.nodes
|
||||||
|
// edges 原来没做空值保护(nodes 有),拿到 undefined 会直接抛
|
||||||
|
const edges = flowchartData?.edges ?? []
|
||||||
|
|
||||||
if (!nodes || nodes.length === 0) {
|
if (!nodes || nodes.length === 0) {
|
||||||
return "graph TD\n A[空流程图]"
|
return "graph TD\n A[空流程图]"
|
||||||
@@ -22,7 +39,9 @@ export function useMermaidConverter() {
|
|||||||
// 处理节点 - 根据原始类型和自定义标签
|
// 处理节点 - 根据原始类型和自定义标签
|
||||||
nodes.forEach((node: any) => {
|
nodes.forEach((node: any) => {
|
||||||
const nodeId = safeId(node.id)
|
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
|
const originalType = node.data?.originalType || node.type
|
||||||
|
|
||||||
// 根据节点原始类型确定Mermaid语法
|
// 根据节点原始类型确定Mermaid语法
|
||||||
@@ -60,10 +79,10 @@ export function useMermaidConverter() {
|
|||||||
edges.forEach((edge: any) => {
|
edges.forEach((edge: any) => {
|
||||||
const source = safeId(edge.source)
|
const source = safeId(edge.source)
|
||||||
const target = safeId(edge.target)
|
const target = safeId(edge.target)
|
||||||
const label = edge.label ?? ""
|
const rawLabel = String(edge.label ?? "")
|
||||||
|
|
||||||
if (label && label.trim() !== "") {
|
if (rawLabel.trim() !== "") {
|
||||||
mermaid += ` ${source} -->|"${label}"| ${target}\n`
|
mermaid += ` ${source} -->|"${escapeLabel(rawLabel)}"| ${target}\n`
|
||||||
} else {
|
} else {
|
||||||
mermaid += ` ${source} --> ${target}\n`
|
mermaid += ` ${source} --> ${target}\n`
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user