效果图如下:
目前只做了绘制部分,绘制方式也比较简单,点击工具栏中需要绘制的图形,在画布上左键点击将会绘制一个图形出来,工具栏选中第一个,再点击其他图像,长按鼠标左键可以移动,删除使用键盘delete键,目前没做批量框选(懒得写了,按照点击选中的思路可以自己实现),工具栏最后一个画线,需要鼠标长按,起点与终点在图形上,路径为自动生成,不能自定义调整,但可以通过拖动节点改变路径。
节点结构如下:
type Node = {
key: number;//标识
type: 1 | 2 | 3 | 4 | 5 | 6;//1开始,2结束,3任务节点,4决策节点,5子流程节点,6连接线
name?: string;//名称
x: number;//坐标x
y: number;//坐标y
radius?: number;//半径
width?: number;//宽
heigth?: number;//高
isCheck: boolean;//是否选中
startNode?: number;//开始节点--作连接线时使用
endNode?: number;//结束节点--作连接线时使用
//连接线线路
ponits?: { start: { x: number, y: number }, center?: { x: number, y: number }[], end: { x: number, y: number }, arrow?: { x: number, y: number }[] };
endPoint?: { x: number, y: number };//结束点--作连接线未完成画虚线时使用
}
如需要传给后端需要调整格式(目前传不了,因为没做节点绑定人),后端的部分后面再做了,有空了再去优化,比如工具栏图标,UI什么的,完整代码如下
index.tsx
import { BranchesOutlined, DragOutlined, } from '@ant-design/icons';
import { Button, Col, Form, Row, Tooltip } from 'antd';
import styles from './index.less'
import React, { MouseEvent, useEffect, useRef, useState } from 'react';
type Node = {
key: number;//标识
type: 1 | 2 | 3 | 4 | 5 | 6;//1开始,2结束,3任务节点,4决策节点,5子流程节点,6连接线
name?: string;//名称
x: number;//坐标x
y: number;//坐标y
radius?: number;//半径
width?: number;//宽
heigth?: number;//高
isCheck: boolean;//是否选中
startNode?: number;//开始节点--作连接线时使用
endNode?: number;//结束节点--作连接线时使用
//连接线线路
ponits?: { start: { x: number, y: number }, center?: { x: number, y: number }[], end: { x: number, y: number }, arrow?: { x: number, y: number }[] };
endPoint?: { x: number, y: number };//结束点--作连接线未完成画虚线时使用
}
const Test: React.FC = () => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const [nodes, setNodes] = useState<Record<number, Node | undefined>>({});
const [checkButton, setCheckButton] = useState<number>();
const [checkNode, setCheckNode] = useState<number>(0);
const [currentConnetLine, setCurrentConnetLine] = useState<number>(0);
const [mouseDown, setMouseDown] = useState<boolean>(false);
/**
* 检查鼠标是否点击到线段
* @param click 鼠标坐标
* @param line 线段起始结束点
* @param width 线段宽度
*/
const checkClickLine = (click: { x: number, y: number }, line: { start: { x: number, y: number }, end: { x: number, y: number } }, width: number): boolean => {
const { x: startX, y: startY } = line.start;
const { x: endX, y: endY } = line.end;
// 计算线段的方向向量
const dx = endX - startX;
const dy = endY - startY;
// 计算线段长度
const length = Math.sqrt(dx * dx + dy * dy);
// 如果线段长度为0,则直接比较点是否相同
if (length === 0) {
const distance = Math.sqrt(Math.pow(click.x - startX, 2) + Math.pow(click.y - startY, 2));
return distance <= width / 2;
}
// 计算点击点到线段所在直线的垂直距离
const u = ((click.x - startX) * dx + (click.y - startY) * dy) / (length * length);
// 检查点击点是否在线段上
if (u < 0 || u > 1) {
return false;
}
// 计算最近点
const xClosest = startX + u * dx;
const yClosest = startY + u * dy;
// 计算点击点到最近点的距离
const distance = Math.sqrt(Math.pow(click.x - xClosest, 2) + Math.pow(click.y - yClosest, 2));
// 检查距离是否小于线段宽度的一半
return distance <= width / 2;
}
const checkNodeSelect = (node: Node, e: MouseEvent): boolean => {
const canvas = canvasRef.current;
if (canvas) {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
switch (node.type) {
case 1:
case 2:
if (node.radius) {
const distance = Math.sqrt(Math.pow(x - node.x, 2) + Math.pow(y - node.y, 2));
return distance <= node.radius
}
break;
case 3:
case 4:
if (node.heigth && node.width) {
const leftTop = { x: node.x - node.width / 2, y: node.y - node.heigth / 2 };
const rightBottom = { x: node.x + node.width / 2, y: node.y + node.heigth / 2 };
return x >= leftTop.x && x <= rightBottom.x && y >= leftTop.y && y <= rightBottom.y;
}
break;
case 5:
if (node.radius) {
const halfSize = node.radius / 2 - 3;
const dx = Math.abs(x - node.x);
const dy = Math.abs(y - node.y);
return dx * dx + dy * dy <= halfSize * halfSize;;
}
break
case 6:
if (node.ponits) {
let check = false;
const ponits = node.ponits;
if (ponits.center) {
for (let i = 0, l = ponits.center.length; i < l; i++) {
if (!check) {
if (i == 0) {
check = checkClickLine({ x, y }, { start: ponits.start, end: ponits.center[i] }, 5);
if (!check) {
check = checkClickLine({ x, y }, { start: ponits.center[i], end: ponits.center[i + 1] }, 5);
}
}
else if (i == l - 1) {
check = checkClickLine({ x, y }, { start: ponits.center[i], end: ponits.end }, 5);
} else {
check = checkClickLine({ x, y }, { start: ponits.center[i], end: ponits.center[i + 1] }, 5);
}
}
}
} else {
check = checkClickLine({ x, y }, { start: ponits.start, end: ponits.end }, 5);
}
return check;
}
break;
}
}
return false
}
const [form] = Form.useForm();
/**
* 获取节点的上下左右几个端点
* @param node
* @param direction 上下左右|1234
*/
const getNodePonit = (node: Node, direction: 1 | 2 | 3 | 4): { x: number, y: number } => {
switch (node.type) {
case 1: case 2:
switch (direction) {
case 1:
return { x: node.x, y: node.y - (node.radius ? node.radius : 0) };
case 2:
return { x: node.x, y: node.y + (node.radius ? node.radius : 0) };
case 3:
return { x: node.x - (node.radius ? node.radius : 0), y: node.y };
case 4:
return { x: node.x + (node.radius ? node.radius : 0), y: node.y };
}
case 5:
switch (direction) {
case 1:
return { x: node.x, y: node.y - (node.radius ? node.radius : 0) / 2 };
case 2:
return { x: node.x, y: node.y + (node.radius ? node.radius : 0) / 2 };
case 3:
return { x: node.x - (node.radius ? node.radius : 0) / 2, y: node.y };
case 4:
return { x: node.x + (node.radius ? node.radius : 0) / 2, y: node.y };
}
case 3: case 4:
switch (direction) {
case 1:
return { x: node.x, y: node.y - (node.heigth ? node.heigth / 2 : 0) };
case 2:
return { x: node.x, y: node.y + (node.heigth ? node.heigth / 2 : 0) };
case 3:
return { x: node.x - (node.width ? node.width / 2 : 0), y: node.y };
case 4:
return { x: node.x + (node.width ? node.width / 2 : 0), y: node.y };
}
}
return { x: 0, y: 0 };
}
/**
* 获取箭头的三个点坐标
* @param node 顶点
* @param type 1,箭头向上,2箭头向下,3,箭头向左,4箭头向右
*/
const getArrow = (node: { x: number, y: number }, type: 1 | 2 | 3 | 4): { x: number, y: number }[] => {
const width = 8;
const height = 14;
switch (type) {
case 1:
return [{ x: node.x, y: node.y }, { x: node.x - width / 2, y: node.y + height }, { x: node.x + width / 2, y: node.y + height }]
case 2:
return [{ x: node.x, y: node.y }, { x: node.x - width / 2, y: node.y - height }, { x: node.x + width / 2, y: node.y - height }]
case 3:
return [{ x: node.x, y: node.y }, { x: node.x + height, y: node.y - width / 2 }, { x: node.x + height, y: node.y + width / 2 }]
case 4:
return [{ x: node.x, y: node.y }, { x: node.x - height, y: node.y - width / 2 }, { x: node.x - height, y: node.y + width / 2 }]
}
}
/**
* 根据连接的两点计算线路点
* @param startNode
* @param endNode
* @returns
*/
const getConnetPoints = (startNode: Node, endNode: Node): { start: { x: number, y: number }, center?: { x: number, y: number }[], end: { x: number, y: number }, arrow?: { x: number, y: number }[] } => {
if (startNode.x === endNode.x) {
if (startNode.y > endNode.y) {
//终点在正上方
const end = getNodePonit(endNode, 2);
return { start: getNodePonit(startNode, 1), end, arrow: getArrow(end, 1) }
} else {
const end = getNodePonit(endNode, 1);
return { start: getNodePonit(startNode, 2), end, arrow: getArrow(end, 2) }
}
} else if (startNode.y === endNode.y) {
if (startNode.x > endNode.x) {
//终点在正左方
const end = getNodePonit(endNode, 4);
return { start: getNodePonit(startNode, 3), end, arrow: getArrow(end, 3) }
} else {
const end = getNodePonit(endNode, 3);
return { start: getNodePonit(startNode, 4), end, arrow: getArrow(end, 4) }
}
} else if (startNode.x > endNode.x) {
if (startNode.y > endNode.y) {
if ((startNode.y - endNode.y) > (startNode.x - endNode.x)) {
const start = getNodePonit(startNode, 1);
const end = getNodePonit(endNode, 2);
const centerY = start.y - (start.y - end.y) / 2;
const center = [{ x: start.x, y: centerY }, { x: end.x, y: centerY }]
const arrow = getArrow(end, 1)
return { start, center, end, arrow }
} else {
const start = getNodePonit(startNode, 3);
const end = getNodePonit(endNode, 4);
const centerX = start.x - (start.x - end.x) / 2;
const center = [{ x: centerX, y: start.y }, { x: centerX, y: end.y }]
const arrow = getArrow(end, 3)
return { start, center, end, arrow }
}
} else {
if ((endNode.y - startNode.y) > (startNode.x - endNode.x)) {
const start = getNodePonit(startNode, 2);
const end = getNodePonit(endNode, 1);
const centerY = end.y - (end.y - start.y) / 2;
const center = [{ x: start.x, y: centerY }, { x: end.x, y: centerY }]
const arrow = getArrow(end, 2)
return { start, center, end, arrow }
} else {
const start = getNodePonit(startNode, 3);
const end = getNodePonit(endNode, 4);
const centerX = start.x - (start.x - end.x) / 2;
const center = [{ x: centerX, y: start.y }, { x: centerX, y: end.y }]
const arrow = getArrow(end, 3)
return { start, center, end, arrow }
}
}
} else {
if (startNode.y > endNode.y) {
if ((startNode.y - endNode.y) > (endNode.x - startNode.x)) {
const start = getNodePonit(startNode, 1);
const end = getNodePonit(endNode, 2);
const centerY = start.y - (start.y - end.y) / 2;
const center = [{ x: start.x, y: centerY }, { x: end.x, y: centerY }]
const arrow = getArrow(end, 1)
return { start, center, end, arrow }
} else {
const start = getNodePonit(startNode, 4);
const end = getNodePonit(endNode, 3);
const centerX = end.x - (end.x - start.x) / 2;
const center = [{ x: centerX, y: start.y }, { x: centerX, y: end.y }]
const arrow = getArrow(end, 4)
return { start, center, end, arrow }
}
} else {
if ((endNode.y - startNode.y) > (endNode.x - startNode.x)) {
const start = getNodePonit(startNode, 2);
const end = getNodePonit(endNode, 1);
const centerY = end.y - (end.y - start.y) / 2;
const center = [{ x: start.x, y: centerY }, { x: end.x, y: centerY }]
const arrow = getArrow(end, 2)
return { start, center, end, arrow }
} else {
const start = getNodePonit(startNode, 4);
const end = getNodePonit(endNode, 3);
const centerX = end.x - (end.x - start.x) / 2;
const center = [{ x: centerX, y: start.y }, { x: centerX, y: end.y }]
const arrow = getArrow(end, 4)
return { start, center, end, arrow }
}
}
}
}
useEffect(() => {
if (canvasRef.current && nodes && Object.values(nodes).length > 0) {
const ctx = canvasRef.current.getContext('2d');
if (ctx) {
ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
Object.values(nodes).map(node => {
if (node) {
switch (node.type) {
case 1:
case 2:
if (node.radius) {
ctx.beginPath();
ctx.arc(node.x, node.y, node.radius, 0, 2 * Math.PI);
ctx.fillStyle = node.type === 1 ? '#90EE90' : '#FFA07A';
ctx.fill();
ctx.lineWidth = 0.5;
ctx.strokeStyle = 'black';
ctx.stroke();
// 添加文字
ctx.fillStyle = '#000000'; // 文字颜色
ctx.font = '12px Arial'; // 文字样式
ctx.textAlign = 'center'; // 文字对齐方式
ctx.fillText(node.type === 1 ? '开始' : '结束', node.x, node.y + 2);
ctx.stroke();
if (node.isCheck) {
// 设定正方形的位置和尺寸
const halfSize = node.radius + 2;
// 绘制正方形的虚线边框
ctx.setLineDash([5, 5]); // 设置虚线样式,5个单位实线,5个单位间隔
ctx.beginPath();
ctx.rect(node.x - halfSize, node.y - halfSize, halfSize * 2, halfSize * 2);
ctx.strokeStyle = 'blue'; // 边框颜色
ctx.stroke();
//绘制完虚线后重置线型为实线
ctx.setLineDash([]);
}
}
break;
case 3:
case 4:
if (node.heigth && node.width) {
ctx.beginPath();
const radius = 20; // 圆角的大小
const x = node.x - node.width / 2;
const y = node.y - node.heigth / 2;
ctx.strokeStyle = node.type == 3 ? '#FF0000' : '#ADD8E6';
// 设置边框宽度
ctx.lineWidth = 0.5;
ctx.moveTo(x + radius, y);
ctx.arcTo(x + node.width, y, x + node.width, y + node.heigth, radius);
ctx.arcTo(x + node.width, y + node.heigth, x, y + node.heigth, radius);
ctx.arcTo(x, y + node.heigth, x, y, radius);
ctx.arcTo(x, y, x + radius, y, radius);
ctx.stroke();
ctx.fillStyle = 'white'; // 矩形颜色
ctx.fill();
// 添加文字
ctx.fillStyle = '#000000'; // 文字颜色
ctx.font = '12px Arial'; // 文字样式
ctx.textAlign = 'left'; // 文字对齐方式
const text = node.name ? node.name : '节点' + node.key;
if (text.length < 8) {
ctx.fillText(text, x + (5 * (8 - text.length)), y + 27);
} else if (text.length < 15) {
ctx.fillText(text.slice(0, 7), x + 8, y + 20);
const twoLine = text.slice(7, text.length);
ctx.fillText(twoLine, x + 8 + (5.7 * (7 - twoLine.length)), y + 38);
} else {
ctx.fillText(text.slice(0, 7), x + 8, y + 20);
const twoLine = text.slice(7, 13) + '...';
ctx.fillText(twoLine, x + 8, y + 38);
}
ctx.stroke();
if (node.isCheck) {
ctx.setLineDash([5, 5]);
ctx.beginPath();
ctx.rect(node.x - node.width / 2 - 2, node.y - node.heigth / 2 - 2, node.width + 4, node.width / 2 + 4);
ctx.strokeStyle = 'blue';
ctx.stroke();
ctx.setLineDash([]);
}
}
break;
case 5:
if (node.radius) {
ctx.lineWidth = 0.5;
ctx.strokeStyle = 'black';
// 开始绘制菱形路径
ctx.beginPath();
ctx.moveTo(node.x, node.y - node.radius / 2);
ctx.lineTo(node.x + node.radius / 2, node.y);
ctx.lineTo(node.x, node.y + node.radius / 2);
ctx.lineTo(node.x - node.radius / 2, node.y);
ctx.closePath();
// 绘制边框
ctx.stroke();
// 设置填充样式并填充颜色
ctx.fillStyle = 'white';
ctx.fill();
// 设置填充样式并填充颜色
ctx.beginPath();
ctx.fillStyle = 'black';
ctx.fill();
ctx.font = '18px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('if', node.x, node.y + 2);
ctx.stroke();
if (node.isCheck) {
const halfSize = node.radius / 2 + 2;
ctx.setLineDash([5, 5]);
ctx.beginPath();
ctx.rect(node.x - halfSize, node.y - halfSize, halfSize * 2, halfSize * 2);
ctx.strokeStyle = 'blue';
ctx.stroke();
ctx.setLineDash([]);
}
}
break
case 6:
if (node.endPoint) {
// 设置虚线样式
ctx.setLineDash([5, 5]); // 第一个数字是实线部分长度,第二个数字是间隔长度
ctx.lineDashOffset = 0; // 虚线偏移量
ctx.lineWidth = 2; // 线条宽度
const x2 = node.x;
const y2 = node.y;
const x1 = node.endPoint.x;
const y1 = node.endPoint.y;
const dx = x2 - x1;
const dy = y2 - y1;
const distanceP1P2 = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
const ratio = 20 / distanceP1P2;
const p3 = { x: x1 + (x2 - x1) * ratio, y: y1 + (y2 - y1) * ratio };
const unitX = dx / distanceP1P2;
const unitY = dy / distanceP1P2;
const nx = -dy / distanceP1P2;
const ny = dx / distanceP1P2;
const p4 = { x: p3.x + nx * 5, y: p3.y + ny * 5 };
const p5 = { x: p3.x - nx * 5, y: p3.y - ny * 5 };
const p6 = { x: x1 + unitX * 5, y: y1 + unitY * 5 };
// 绘制箭头主体(直线)
ctx.beginPath();
ctx.moveTo(node.x, node.y);
ctx.lineTo(p6.x, p6.y);
ctx.stroke();
// 计算顶点p1到中垂线起点p2的向量
ctx.setLineDash([]);
ctx.beginPath();
ctx.moveTo(node.endPoint.x, node.endPoint.y);
ctx.lineTo(p4.x, p4.y);
ctx.lineTo(p5.x, p5.y);
ctx.closePath();
ctx.fillStyle = 'black'; // 箭头颜色
ctx.fill();
// 重置虚线样式,如果之后还需要绘制实线或其他虚线样式
} else {
if (node.ponits) {
console.log(node.isCheck)
const ponits = node.ponits;
ctx.lineWidth = 2; // 线条宽度
ctx.strokeStyle = node.isCheck ? 'red' : 'black',
ctx.beginPath();
ctx.moveTo(ponits.start.x, ponits.start.y);
if (ponits.center) {
ponits.center.map(n => {
ctx.lineTo(n.x, n.y);
})
}
ctx.lineTo(ponits.end.x, ponits.end.y);
ctx.stroke();
if (ponits.arrow) {
ctx.beginPath();
ctx.lineWidth = 2;
ctx.moveTo(ponits.arrow[0].x, ponits.arrow[0].y);
ctx.lineTo(ponits.arrow[1].x, ponits.arrow[1].y);
ctx.lineTo(ponits.arrow[2].x, ponits.arrow[2].y);
ctx.closePath();
ctx.fillStyle = node.isCheck ? 'red' : 'black', // 箭头颜色
ctx.fill();
}
}
}
break;
}
}
})
}
}
}, [nodes])
const onMouseDown = (e: MouseEvent) => {
const canvas = canvasRef.current;
if (e.button === 0) {
setMouseDown(true)
} else {
setMouseDown(false)
}
if (canvas) {
const rect = canvas.getBoundingClientRect();
const key = Object.values(nodes).length + 1;
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
switch (checkButton) {
case 0:
if (e.button === 0) {
let check = false;
Object.values(nodes).map(node => {
if (node) {
if (!check && checkNodeSelect(node, e)) {
setCheckNode(node.key);
node.isCheck = true;
check = true;
} else {
node.isCheck = false;
}
}
})
if (!check) {
setCheckNode(0)
}
setNodes({ ...nodes })
} else {
setCheckNode(0);
}
break
case 1:
case 2:
if (e.button === 0) {
nodes[key] = { key, name: checkButton === 1 ? '开始' : '结束', type: checkButton, x, y, radius: 20, isCheck: false };
setNodes({ ...nodes })
}
break
case 3:
case 4:
if (e.button === 0) {
nodes[key] = { key, name: checkButton === 3 ? '任务节点' : '子流程节点', type: checkButton, x, y, isCheck: false, width: 100, heigth: 50 };
setNodes({ ...nodes })
}
break
case 5:
nodes[key] = { key, type: checkButton, name: '决策节点', x, y, isCheck: false, radius: 40 };
setNodes({ ...nodes })
break
case 6:
const currentConnetLine: Node = { key, type: checkButton, name: '连接线', x, y, isCheck: false }
setCurrentConnetLine(key)
let check = false;
Object.values(nodes).map(node => {
if (node) {
if (!check && checkNodeSelect(node, e)) {
currentConnetLine.startNode = node.key
check = true;
}
}
})
nodes[key] = currentConnetLine;
setNodes({ ...nodes })
break;
}
}
};
const onMouseMove = (e: MouseEvent) => {
const canvas = canvasRef.current;
if (canvas && mouseDown) {
const rect = canvas.getBoundingClientRect();
switch (checkButton) {
case 0:
const node = nodes[checkNode];
if (node) {
node.x = e.clientX - rect.left;
node.y = e.clientY - rect.top;
//重新计算连接线
Object.values(nodes).map(node => {
if (node?.type === 6 && node.startNode && node.endNode) {
const startNode = nodes[node.startNode];
const endNode = nodes[node.endNode];
if (startNode && endNode) {
node.ponits = getConnetPoints(startNode, endNode)
}
}
});
setNodes({ ...nodes })
}
break;
case 6:
const lineNode = nodes[currentConnetLine];
if (lineNode) {
lineNode.endPoint = { x: e.clientX - rect.left, y: e.clientY - rect.top }
setNodes({ ...nodes })
}
break;
}
}
};
const handleKeyDown = (event: any) => {
if (event.key === 'Delete' && checkNode) {
nodes[checkNode] = undefined
setNodes({ ...nodes })
}
};
useEffect(() => {
document.addEventListener('keydown', handleKeyDown);
return () => {
document.removeEventListener('keydown', handleKeyDown);
};
}, [checkNode]);
const onMouseUp = (e: MouseEvent) => {
if (e.button === 0) {
switch (checkButton) {
case 6:
const canvas = canvasRef.current;
const connetLine = nodes[currentConnetLine];
if (canvas && connetLine) {
if (!connetLine.startNode) {
nodes[currentConnetLine] = undefined;
setCurrentConnetLine(0)
setNodes({ ...nodes })
return;
}
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
if (x === connetLine.x && y === connetLine.y) {
nodes[currentConnetLine] = undefined;
setCurrentConnetLine(0)
} else {
let check = false;
Object.values(nodes).map(node => {
if (node) {
if (!check && checkNodeSelect(node, e) && connetLine.startNode !== node.key) {
connetLine.endNode = node.key;
check = true;
}
}
})
if (!check) {
nodes[currentConnetLine] = undefined;
} else {
connetLine.endPoint = undefined;
if (connetLine.endNode) {
const startNode = nodes[connetLine.startNode];
const endNode = nodes[connetLine.endNode];
if (startNode && endNode) {
connetLine.ponits = getConnetPoints(startNode, endNode)
nodes[currentConnetLine] = connetLine;
}
}
setCurrentConnetLine(0)
}
setNodes({ ...nodes })
}
break;
}
break;
}
setMouseDown(false)
}
};
const changeButton = (btn: number) => {
setCheckButton(btn);
switch (btn) {
case 1: case 2: case 3: case 4: case 5:
setCheckNode(0);
Object.values(nodes).map(node => {
if (node)
node.isCheck = false;
})
setNodes({ ...nodes })
break;
}
}
const typeStr = (type: number) => {
switch (type) {
case 1: return '开始节点';
case 2: return '结束节点';
case 3: return '任务节点';
case 4: return '子流程节点';
case 5: return '决策节点';
case 6: return '连接线';
}
}
return (<Row gutter={24}>
<Col span={6}>
<table className={styles.canvasTable}>
<thead>
<tr>
<th colSpan={2} >
<p style={{ paddingTop: 10 }}>
绘制工具
</p>
</th>
</tr>
</thead>
<tbody >
<tr>
<td style={{ backgroundColor: checkButton === 0 ? '#948f8f' : '' }} onClick={() => { changeButton(0) }} >
<Tooltip title='选择'>
<Button onClick={() => { changeButton(0) }} icon={<DragOutlined />} />
</Tooltip>
</td>
<td style={{ backgroundColor: checkButton === 1 ? '#948f8f' : '' }} onClick={() => { changeButton(1) }} >
<Tooltip title='开始节点'>
<Button onClick={() => { changeButton(1) }} shape="circle" style={{ backgroundColor: '#90EE90' }}><p style={{ fontSize: '11px', paddingTop: '10px' }}>开始</p></Button>
</Tooltip>
</td>
</tr>
<tr>
<td style={{ backgroundColor: checkButton === 2 ? '#948f8f' : '' }} onClick={() => { changeButton(2) }}>
<Tooltip title='结束节点'>
<Button onClick={() => { changeButton(2) }} shape="circle" style={{ backgroundColor: '#FFA07A' }} ><p style={{ fontSize: '11px', paddingTop: '10px' }}>结束</p></Button>
</Tooltip>
</td>
<td style={{ backgroundColor: checkButton === 5 ? '#948f8f' : '' }} onClick={() => { changeButton(5) }}>
<Tooltip title='决策节点'>
<div className={styles.rotatedSquare} onClick={() => { changeButton(5) }}>if</div>
</Tooltip>
</td>
</tr>
<tr>
<td style={{ backgroundColor: checkButton === 3 ? '#948f8f' : '' }} onClick={() => { changeButton(3) }}>
<Tooltip title='任务节点'>
<Button onClick={() => { changeButton(3) }} shape="round" style={{ border: '1px solid #FF0000', width: '50px' }}>
<p style={{ fontSize: '11px', paddingTop: '10px' }}>任务</p>
</Button>
</Tooltip>
</td>
<td style={{ backgroundColor: checkButton === 4 ? '#948f8f' : '' }} onClick={() => { changeButton(4) }}>
<Tooltip title='子流程节点' >
<Button onClick={() => { changeButton(4) }} shape="round" style={{ border: '1px solid #ADD8E6', width: '50px' }}>
<p style={{ fontSize: '11px', paddingTop: '10px' }}>子流程</p>
</Button>
</Tooltip>
</td>
</tr>
<tr>
<td style={{ backgroundColor: checkButton === 6 ? '#948f8f' : '' }} onClick={() => { changeButton(6) }} >
<Tooltip title='连接节点'>
<Button onClick={() => { changeButton(6) }} icon={<BranchesOutlined />} />
</Tooltip>
</td>
<td>
</td>
</tr>
</tbody>
</table>
<table className={styles.canvasTable}>
<thead>
<tr>
<th colSpan={2} >
<p style={{ paddingTop: 10 }}>
节点属性
</p>
</th>
</tr>
</thead>
<tbody >
<tr>
<td style={{ width: '80px' }}>节点名称</td>
<td >
{(checkNode !== 0 && nodes[checkNode]) && (<div>{nodes[checkNode].name}</div>)}
</td>
</tr>
<tr>
<td >节点类型</td>
<td >
{(checkNode !== 0 && nodes[checkNode]) && (<div>{typeStr(nodes[checkNode].type)}</div>)}
</td>
</tr>
</tbody>
</table>
</Col>
<Col span={18}>
<canvas onMouseDown={onMouseDown}
onMouseMove={onMouseMove}
onMouseUp={onMouseUp}
ref={canvasRef}
width='700px'
height='400px'
style={{ border: '1px solid #000' }}
/>
</Col>
</Row>
)
}
export default Test;
index.less:
.rotatedSquare {
background-color: rgb(216, 218, 223);
padding-top: 2px;
margin-top: 2px;
margin-left: 30px;
cursor: pointer;
height: 26px;
width: 30px;
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
}
.canvasTable{
border: 1px solid #000;
width: 100%;
}
.canvasTable th{
text-align: center;
align-items: center;
}
.canvasTable td{
padding: 5px;
text-align: center;
align-items: center;
border: 1px solid #000
}