赞
踩
之前的几个demo都是在vue3中写的,虽然可以直接拿去复用。
但是根据有些看客反馈,想用一个vue2版本的,毕竟很多人开发功能的时间都不是特别富裕。大多时候还是用现成的demo更好一些。
这里我就写一个简易版本的demo,可以实现绘制,并且删除连接线和节点等功能,篇幅也不大,适合应急的朋友,哈哈
- export const readyConfig = {
- Container: 'plumbBox',
- anchor: ['Bottom', 'Top', 'Left', 'Right'],
- connector: 'Straight',
- endpoint: 'Blank',
- PaintStyle: { stroke: '#8b8c8d', strokeWidth: 2, outlineStroke: 'transparent', outlineWidth: 10 },
- Overlays: [['Arrow', { width: 10, length: 5, location: 0.5, direction: 1 }]],
- endpointStyle: { fill: 'lightgray', outlineStroke: 'darkgray', outlineWidth: 2 }
- }
- export const sourceConfig = {
- filter: '.pointNode',
- filterExclude: false,
- allowLoopback: true,
- maxConnections: -1,
- Container: 'plumbBox',
- anchor: ['Bottom', 'Top', 'Left', 'Right'],
- connector: 'Straight',
- endpoint: 'Blank',
- PaintStyle: { stroke: '#8b8c8d', strokeWidth: 2, outlineStroke: 'transparent', outlineWidth: 10 },
- Overlays: [['Arrow', { width: 10, length: 5, location: 0.5, direction: 1 }]],
- endpointStyle: { fill: 'lightgray', outlineStroke: 'darkgray', outlineWidth: 2 }
- }
- export const targetConfig = {
- filter: '.pointNode',
- filterExclude: false,
- allowLoopback: true,
- maxConnections: -1,
- Container: 'plumbBox',
- anchor: ['Bottom', 'Top', 'Left', 'Right'],
- connector: 'Straight',
- endpoint: 'Blank',
- PaintStyle: { stroke: '#8b8c8d', strokeWidth: 2, outlineStroke: 'transparent', outlineWidth: 10 },
- Overlays: [['Arrow', { width: 10, length: 5, location: 0.5, direction: 1 }]],
- endpointStyle: { fill: 'lightgray', outlineStroke: 'darkgray', outlineWidth: 2 }
- }

其实这些配置都大差不差,之所以分别罗列,是让大家搞明白,画布层级的配置,起点节点的配置以及终点节点的配置都是可以单独去配置,具体配置项可以看我之前的文档。
- export const leftMenuList = [
- {
- name: "节点1",
- id: "app1",
- },
- {
- name: "节点2",
- id: "app2",
- },
- {
- name: "节点3",
- id: "app3",
- },
- {
- name: "节点4",
- id: "app4",
- },
- ]

这里的数据其实是左侧的数据
- <template>
- <div class="flowBox">
- <div class="leftMenu">
- <h4 @click="checkInfo">左侧菜单</h4>
- <draggable @start="start" @end="end" :sort="false">
- <div
- v-for="(item, index) in leftMenuList"
- :key="item.id"
- @mousedown="(el) => downNode(el, item)"
- class="leftNode"
- >
- {{ item.name }}
- </div>
- <h4>操作提示</h4>
- <hr />
- <p>左侧拖拽至右侧画布</p>
- <p>右键节点是删除节点,左键线条是删除线条</p>
- </draggable>
- </div>
- <div class="plumbBox" id="plumbBox">
- <div
- v-for="(item, index) in dataInfo"
- :key="item.id"
- :id="item.id"
- :style="nodeStyle(item)"
- @click.right="deleteNode($event, item)"
- >
- {{ item.name }}
- <div class="pointNode"></div>
- </div>
- </div>
- </div>
- </template>
- <script>
- import { jsPlumb } from "jsplumb";
- import { readyConfig, sourceConfig, targetConfig } from "./config";
- import { leftMenuList } from "./data";
- import { cloneDeep } from "lodash";
- import draggable from "vuedraggable";
- export default {
- name: "flowChart",
- components: { draggable },
- data() {
- return {
- //节点列表
- leftMenuList: leftMenuList,
- dataInfo: [],
- //plumb实例
- PlumbInit: null,
- //关系列表
- renderList: [],
- //画布信息(大小和位置)
- canvasInfo: null,
- //鼠标位置精准判定
- nodePositionDiff: null,
- //选中的左侧列表节点
- ativeNodeItem: null,
- //线条的信息
- deleteLineInfo: null,
- };
- },
- mounted() {
- this.jsPlumbInit();
- this.readyPlumbDataFun("once");
- },
- methods: {
- //初始化
- jsPlumbInit() {
- this.PlumbInit = jsPlumb.getInstance();
- this.PlumbInit.importDefaults(readyConfig);
- },
- //组织渲染用的数据
- readyPlumbDataFun(flag) {
- this.renderList = [];
- this.PlumbInit.deleteEveryConnection();
- this.PlumbInit.deleteEveryEndpoint();
- this.$nextTick(() => {
- this.dataInfo = cloneDeep(this.dataInfo);
- //根据数据创建关联信息(连线关系)
- this.dataInfo.forEach((item) => {
- if (item.to && item.to.length > 0) {
- item.to.forEach((item1) => {
- let nodeConfig = Object.assign(
- {
- source: item.id,
- target: item1,
- },
- readyConfig
- );
- this.renderList.push(nodeConfig);
- });
- }
- this.makeNodeConfig(item);
- });
- this.readyPlumbNodeFun(flag);
- });
- },
- //配置节点的具体信息
- makeNodeConfig(item) {
- this.PlumbInit.setSourceEnabled(item.id, true);
- this.PlumbInit.setTargetEnabled(item.id, true);
- this.PlumbInit.makeSource(item.id, sourceConfig);
- this.PlumbInit.makeTarget(item.id, targetConfig);
- this.PlumbInit.setDraggable(item.id, true);
- this.PlumbInit.draggable(item.id, {
- containment: "parent",
- stop: function (el) {
- item.left = el.pos[0];
- item.top = el.pos[1];
- },
- });
- },
- //渲染页面关系
- readyPlumbNodeFun(flag) {
- this.PlumbInit.ready(() => {
- this.renderList.forEach((item) => {
- this.PlumbInit.connect(item);
- });
- if (flag !== "once") {
- return;
- }
- //连线事件
- this.PlumbInit.bind("connection", (info) => {
- const sourceNode = this.dataInfo.find(
- (item) => item.id === info.sourceId
- );
- if (sourceNode.to.includes(info.targetId)) {
- return false;
- }
- console.log("调用了几次");
- sourceNode.to.push(info.targetId);
- // this.$nextTick(() => {
- // this.readyPlumbDataFun()
- // })
- return true;
- });
- //点击线条
- this.PlumbInit.bind("click", (con) => {
- this.deleteLineInfo = {
- source: con.sourceId,
- target: con.targetId,
- };
- this.deleteLine(this.deleteLineInfo);
- });
- });
- },
- //plumbNode的样式
- nodeStyle(item) {
- return {
- position: "absolute",
- left: item.left + "px",
- top: item.top + "px",
- width: "200px",
- height: "40px",
- lineHeight: "40px",
- textAlign: "center",
- borderLeft: "2px solid blue",
- borderRadius: "4%",
- cursor: "pointer",
- boxShadow: "#eee 3px 3px 3px 3px",
- };
- },
- //拖动开始
- start() {},
- //拖动结束
- end(e) {
- this.refreshCanvas();
- // 判断位置
- this.judgePosition(
- this.ativeNodeItem,
- this.canvasInfo,
- e.originalEvent.x,
- e.originalEvent.y
- );
- },
- //添加节点
- addNode(positionInfo, nodeInfo) {
- if (this.dataInfo.find((item) => item.id === nodeInfo.id)) {
- this.$message.error("该节点已经存在");
- return;
- }
- this.dataInfo.push({
- name: nodeInfo.name,
- id: nodeInfo.id,
- left: positionInfo.left,
- top: positionInfo.top,
- to: [],
- });
- this.$nextTick(() => {
- this.readyPlumbDataFun();
- });
- },
- //删除节点
- deleteNode($event, nodeInfo) {
- $event.returnValue = false;
- let index = this.dataInfo.findIndex((item) => item.id === nodeInfo.id);
- this.dataInfo.splice(index, 1);
- this.readyPlumbDataFun();
- },
- //删除线
- deleteLine(deleteLineInfo) {
- let dataInfo = cloneDeep(this.dataInfo);
- let node = dataInfo.find((val) => val.id === deleteLineInfo.source);
- let index = node.to.findIndex((val) => val === deleteLineInfo.target);
- node.to.splice(index, 1);
- this.dataInfo = null;
- this.dataInfo = dataInfo;
- this.readyPlumbDataFun();
- },
- checkInfo() {
- console.log(this.dataInfo, "dataInfo");
- console.log(this.renderList, "渲染关系");
- },
- //获取画布信息
- refreshCanvas() {
- this.canvasInfo = document
- .querySelector("#plumbBox")
- .getBoundingClientRect();
- },
- //判断节点拖拽位置
- judgePosition(dragNodeInfo, plumbBoxPositionInfo, x, y) {
- if (
- x - this.nodePositionDiff.leftDiff < plumbBoxPositionInfo.left ||
- x + 200 - this.nodePositionDiff.leftDiff > plumbBoxPositionInfo.right ||
- y - this.nodePositionDiff.topDiff < plumbBoxPositionInfo.top ||
- y + 40 - this.nodePositionDiff.topDiff > plumbBoxPositionInfo.bottom
- ) {
- this.$message.error("节点不能拖拽至画布之外");
- } else {
- const positionInfo = {
- top: y - plumbBoxPositionInfo.top - this.nodePositionDiff.topDiff,
- left: x - plumbBoxPositionInfo.left - this.nodePositionDiff.leftDiff,
- };
- this.addNode(positionInfo, dragNodeInfo);
- }
- },
- //鼠标抬起时,距离判定
- downNode(el, nodeItem) {
- this.ativeNodeItem = nodeItem;
- const mousedownPositionInfo = { x: el.clientX, y: el.clientY };
- // 被拖拽节点初始的位置信息
- const moveBoxBeforePosition = {
- x: el.target.getBoundingClientRect().x,
- y: el.target.getBoundingClientRect().y,
- };
- this.nodePositionDiff = {
- leftDiff: mousedownPositionInfo.x - moveBoxBeforePosition.x,
- topDiff: mousedownPositionInfo.y - moveBoxBeforePosition.y,
- };
- console.log(this.nodePositionDiff, "位置判定");
- },
- },
- };
- </script>
- <style scoped>
- .flowBox {
- display: flex;
- height: 100%;
- }
-
- .leftMenu {
- width: 300px;
- /* height: 100%; */
- border: 1px solid #1a1919;
- }
-
- h4 {
- margin-top: 10px;
- margin-left: 110px;
- }
-
- .leftNode {
- width: 200px;
- height: 40px;
- line-height: 40px;
- text-align: center;
- margin: 30px;
- border: dashed 1px #362c2c;
- cursor: move;
- }
-
- .plumbBox {
- flex: 1;
- /* height: 100%; */
- position: relative;
- }
- .pointNode {
- border-radius: 50%;
- width: 10px;
- height: 10px;
- background: royalblue;
- position: absolute;
- bottom: -5px;
- left: 95px;
- }
- </style>

各个函数的注释都写好了,操作方法也在其中,最主要的是根据插件暴露的api去领悟其中的每一个方法。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。