|
@@ -17,7 +17,8 @@ import Stadium from "@/components/flowchart/terminator";
|
|
|
import DoubleCircle from "@/components/er/multivaluedAttribute";
|
|
|
import Circle from "@/components/flowchart/onPageReference";
|
|
|
import Diamond from "@/components/flowchart/decision";
|
|
|
-import { useModel } from "umi";
|
|
|
+import { isMermaidFlowchart } from "@/utils";
|
|
|
+import mermaid from "mermaid";
|
|
|
|
|
|
export enum VERTEX_TYPE {
|
|
|
RECTANGLE = "rectangle", // 矩形
|
|
@@ -41,7 +42,6 @@ export default forwardRef(function MermaidModal(
|
|
|
) {
|
|
|
const [open, setOpen] = useState(false);
|
|
|
const [mermaidCode, setMermaidCode] = useState("");
|
|
|
- const { graph } = useModel("graphModel");
|
|
|
const resultRef = useRef<MermaidResult>();
|
|
|
|
|
|
useImperativeHandle(ref, () => ({
|
|
@@ -65,7 +65,7 @@ export default forwardRef(function MermaidModal(
|
|
|
const idMap: Record<string, string> = {};
|
|
|
list.forEach((item) => {
|
|
|
// 节点处理
|
|
|
- if (item.type !== "arrow") {
|
|
|
+ if (item.type !== "arrow" && item.type !== "line") {
|
|
|
switch (item.type) {
|
|
|
case VERTEX_TYPE.ROUND: {
|
|
|
comp = Round;
|
|
@@ -97,8 +97,8 @@ export default forwardRef(function MermaidModal(
|
|
|
...comp.node,
|
|
|
id,
|
|
|
position: {
|
|
|
- x: item.x,
|
|
|
- y: item.y,
|
|
|
+ x: parseInt(item.x),
|
|
|
+ y: parseInt(item.y),
|
|
|
},
|
|
|
width: parseInt(item.width),
|
|
|
height: parseInt(item.height),
|
|
@@ -113,11 +113,12 @@ export default forwardRef(function MermaidModal(
|
|
|
};
|
|
|
idMap[item.id] = id;
|
|
|
cells.push(node);
|
|
|
- } else {
|
|
|
+ }
|
|
|
+ if (item.type === "arrow") {
|
|
|
// 连线处理
|
|
|
const edge = {
|
|
|
- source: { cell: idMap[item.start.id] },
|
|
|
- target: { cell: idMap[item.end.id] },
|
|
|
+ source: { cell: idMap[item.start?.id] },
|
|
|
+ target: { cell: idMap[item.end?.id] },
|
|
|
labels: item?.label
|
|
|
? [
|
|
|
{
|
|
@@ -137,6 +138,70 @@ export default forwardRef(function MermaidModal(
|
|
|
return cells;
|
|
|
};
|
|
|
|
|
|
+ // Fallback to Svg
|
|
|
+ const convertSvgToGraphImage = (svgContainer: HTMLDivElement) => {
|
|
|
+ // Extract SVG width and height
|
|
|
+ // TODO: make width and height change dynamically based on user's screen dimension
|
|
|
+ const svgEl = svgContainer.querySelector("svg");
|
|
|
+ if (!svgEl) {
|
|
|
+ throw new Error("SVG element not found");
|
|
|
+ }
|
|
|
+ const rect = svgEl.getBoundingClientRect();
|
|
|
+ const width = rect.width;
|
|
|
+ const height = rect.height;
|
|
|
+
|
|
|
+ // Set width and height explictly since in firefox it gets set to 0
|
|
|
+ // if the width and height are not expilcitly set
|
|
|
+ // eg in some cases like er Diagram, gnatt, width and height is set as 100%
|
|
|
+ // which sets the dimensions as 0 in firefox and thus the diagram isn't rendered
|
|
|
+ svgEl.setAttribute("width", `${width}`);
|
|
|
+ svgEl.setAttribute("height", `${height}`);
|
|
|
+
|
|
|
+ // Convert SVG to image
|
|
|
+ const mimeType = "image/svg+xml";
|
|
|
+ const decoded = unescape(encodeURIComponent(svgEl.outerHTML));
|
|
|
+ const base64 = btoa(decoded);
|
|
|
+ const dataURL = `data:image/svg+xml;base64,${base64}`;
|
|
|
+
|
|
|
+ return {
|
|
|
+ type: "graphImage",
|
|
|
+ mimeType,
|
|
|
+ dataURL,
|
|
|
+ width,
|
|
|
+ height,
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleSvgToImage = (svg: string) => {
|
|
|
+ // Append Svg to DOM
|
|
|
+ const svgContainer = document.createElement("div");
|
|
|
+ svgContainer.setAttribute(
|
|
|
+ "style",
|
|
|
+ `opacity: 0; position: relative; z-index: -1;`
|
|
|
+ );
|
|
|
+ svgContainer.innerHTML = svg;
|
|
|
+ svgContainer.id = "mermaid-diagram";
|
|
|
+ document.body.appendChild(svgContainer);
|
|
|
+
|
|
|
+ const res = convertSvgToGraphImage(svgContainer);
|
|
|
+ svgContainer.remove();
|
|
|
+
|
|
|
+ resultRef.current = {
|
|
|
+ type: "image",
|
|
|
+ data: res?.dataURL,
|
|
|
+ width: res.width,
|
|
|
+ height: res.height,
|
|
|
+ };
|
|
|
+ const img = new Image();
|
|
|
+ img.src = res?.dataURL;
|
|
|
+ img.onload = () => {
|
|
|
+ if (mermaidRef.current) {
|
|
|
+ mermaidRef.current.innerHTML = "";
|
|
|
+ mermaidRef.current.appendChild(img);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
/**
|
|
|
* 绘制图标
|
|
|
*/
|
|
@@ -144,7 +209,7 @@ export default forwardRef(function MermaidModal(
|
|
|
// 获取渲染后的 svg
|
|
|
try {
|
|
|
resultRef.current = undefined;
|
|
|
- const { elements, files } = await parseMermaidToExcalidraw(mermaidCode);
|
|
|
+ const { elements, files } = await parseMermaidToExcalidraw(mermaidCode.replaceAll('"', "'"));
|
|
|
console.log("parse elements:", elements, files);
|
|
|
// 转换失败,加载图片
|
|
|
if (files) {
|
|
@@ -163,11 +228,16 @@ export default forwardRef(function MermaidModal(
|
|
|
}
|
|
|
};
|
|
|
} else {
|
|
|
+ // 类图与时序图处理
|
|
|
+ if(!isMermaidFlowchart(mermaidCode)) {
|
|
|
+ const { svg } = await mermaid.render("mermaid", mermaidCode);
|
|
|
+ handleSvgToImage(svg);
|
|
|
+ return;
|
|
|
+ }
|
|
|
// 转换json
|
|
|
if (mermaidRef.current) {
|
|
|
mermaidRef.current.innerHTML = "";
|
|
|
const cells = toX6Json(elements);
|
|
|
- console.log("parse cells:", cells);
|
|
|
resultRef.current = { type: "cell", data: cells };
|
|
|
const graphInstance = new Graph({
|
|
|
container: mermaidRef.current,
|
|
@@ -201,7 +271,7 @@ export default forwardRef(function MermaidModal(
|
|
|
};
|
|
|
|
|
|
useEffect(() => {
|
|
|
- if (mermaidCode) {
|
|
|
+ if (mermaidCode.trim()) {
|
|
|
drawDiagram();
|
|
|
} else {
|
|
|
if (mermaidRef.current) {
|
|
@@ -247,7 +317,7 @@ export default forwardRef(function MermaidModal(
|
|
|
</i>
|
|
|
</div>
|
|
|
<div className="flex gap-24px">
|
|
|
- <div className="left flex-1 rounded-4px h-500px border-solid border-gray-200">
|
|
|
+ <div className="left flex-1 rounded-4px h-500px border-solid border-gray-200 overflow-y-auto">
|
|
|
<Input.TextArea
|
|
|
autoSize={{ minRows: 10 }}
|
|
|
placeholder="请输入Mermaid代码"
|