1.实现:流程步骤使用dom元素,连接线使用leaf画出 实现如下
1.提供对应流程步骤元素 并设置样式
<div ref="xxxRef" id="xxx" style="width: 500px; height: 500px">
<div flowId="a" flowTo="b,c" class="flow" style="top: 50px; left: 180px; background-color: #0bdb96">订单审批</div>
<div flowId="b" flowTo="d" class="flow" style="top: 210px; left: 50px; background-color: #f1f1f1">大额审批</div>
<div flowId="c" flowTo="d" class="flow" style="top: 210px; left: 300px; background-color: #00bffff2">小额审批</div>
<div flowId="d" class="flow" style="top: 400px; left: 180px; background-color: #00bffff2">最终确认</div>
</div>
<style>
.flow {
position: absolute;
width: 100px;
height: 30px;
line-height: 30px;
border: 1px solid #ccc;
border-radius: 5px;
text-align: center;
font-size: 14px;
padding: 10px;
z-index: 99;
}
#xxx {
position: relative;
border: 1px solid #ccc;
}
</style>
使用leaf 根据定位得到路径 最后画出路径
import { Leafer } from "leafer-draw"
import { Arrow } from "@leafer-in/arrow"
interface flowObjT {
points: pointsT
to: string
id: string
x: number
y: number
width: number
height: number
stroke?: string
strokeWidth?: string
}
interface pointsT {
top: { x: number; y: number }
left: { x: number; y: number }
right: { x: number; y: number }
bottom: { x: number; y: number }
}
export function createFlow(ele: HTMLElement, drection: "row" | "col" = "col") {
if (!ele) {
throw new Error("please provide an element")
}
const LF = new Leafer({
view: ele, // 支持 window 、div、canvas 标签对象, 可使用id字符串(不用加 # 号)
// type: "draw",
// wheel: { zoomMode: false },
move: {
disabled: true,
},
})
const eles = ele.querySelectorAll(".flow") as unknown as HTMLElement[]
const flowDict: { [property: string]: flowObjT } = {}
for (let i = 0; i < eles.length; i++) {
let x: number = eles[i].offsetLeft
let y: number = eles[i].offsetTop
let width: number = eles[i].offsetWidth
let height: number = eles[i].offsetHeight
flowDict[eles[i].getAttribute("flowId") || i.toString()] = {
id: eles[i].getAttribute("flowId") || i.toString(),
to: eles[i].getAttribute("flowTo") || "",
x,
y,
points: {
top: { x: x + width / 2, y: y },
right: { x: x + width, y: y + height / 2 },
bottom: { x: x + width / 2, y: y + height },
left: { x: x, y: y + height / 2 },
},
width,
height,
stroke: eles[i].getAttribute("flowColor") || ele.getAttribute("flowColor") || "green",
strokeWidth: eles[i].getAttribute("flowWidth") || ele.getAttribute("flowWidth") || "5",
}
}
const pathsList: { id: string; paths: number[] }[] = []
for (let x in flowDict) {
if (flowDict[x].to) {
for (let y in flowDict) {
if (flowDict[x].to.includes(y)) {
let begin = drection == "col" ? flowDict[x].points.bottom : flowDict[x].points.right
let end = drection == "col" ? flowDict[y].points.top : flowDict[y].points.left
if (begin.x == end.x || begin.y == end.y) {
pathsList.push({ id: x, paths: [begin.x, begin.y, end.x, end.y] })
} else {
if (drection == "col") {
let a = { x: begin.x, y: begin.y + (end.y - begin.y) / 2 }
let b = { x: begin.x + (end.x - begin.x), y: begin.y + (end.y - begin.y) / 2 }
pathsList.push({ id: x, paths: [begin.x, begin.y, a.x, a.y, b.x, b.y, end.x, end.y] })
} else {
let a = { x: begin.x + (end.x - begin.x) / 2, y: begin.y }
let b = { x: begin.x + (end.x - begin.x) / 2, y: begin.y + (end.y - begin.y) }
pathsList.push({ id: x, paths: [begin.x, begin.y, a.x, a.y, b.x, b.y, end.x, end.y] })
}
}
}
}
}
}
pathsList.forEach((x) => {
LF.add(
new Arrow({
points: x.paths,
endArrow: "arrow",
cornerRadius: flowDict[x.id].strokeWidth,
stroke: flowDict[x.id].stroke,
strokeWidth: flowDict[x.id].strokeWidth,
})
)
})
return (paths: number[]) => {
LF.add(new Arrow({ points: paths, endArrow: "arrow", cornerRadius: 5, strokeWidth: 3, stroke: "rgb(50,205,121)" }))
}
}
元素可绑定width,color属性定义路径颜色,方法返回一个函数 可以用于自定义路径 可以实现复杂路径
效果如下:
仅提供思路,没啥难度