index.vue
<div className="compact-box-tree-page">
<div className="compact-box-tree-canvas" ref="buildCore"></div>
</div>
<script lang="ts">
import { defineComponent, ref, watch, onMounted } from 'vue';
import { mockData } from './config';
import { TreeCanvas } from 'butterfly-dag';
import 'butterfly-dag/dist/index.css';
export default defineComponent({
name: 'Butterfly',
components: {},
setup() {
const buildCore = ref<HTMLDivElement | null>(null);
let canvas: any = null;
onMounted(() => {
if (buildCore.value != null) {
canvas = new TreeCanvas({
root: buildCore.value,
disLinkable: true, // 可删除连线
linkable: true, // 可连线
draggable: true, // 可拖动
zoomable: true, // 可放大
moveable: true, // 可平移
theme: {
edge: {
shapeType: 'Manhattan',
arrow: true,
},
},
layout: {
type: 'compactBox',
options: {
direction: 'TB',
getHeight(d: any) {
return 60;
},
getWidth(d: any) {
return 120;
},
getHGap(d: any) {
return 20;
},
getVGap(d: any) {
return 80;
},
},
},
});
canvas.draw(mockData, {}, () => {
canvas.focusCenterWithAnimate();
});
}
});
const data = ref(mockData);
watch(
() => data,
() => {
canvas.draw(mockData, {}, () => {
canvas.focusCenterWithAnimate();
});
},
);
return {
buildCore,
};
},
});
</script>
<style lang="less">
@butterfly-theme-color-base: #fff;
@butterfly-normal-font-color-base: #222;
@butterfly-overlay-font-color-base: #fff;
@butterfly-primary-color-base: #f66902;
@butterfly-box-border-color-base: #d9d9d9;
@butterfly-box-shadow-base:0 2px 3px 0 rgba(0,112,204,0.06);
@butterfly-box-radius-base: 100px;
@butterfly-line-color-base: #bfbfbf;
// 主题背景色(画布背景色)
@butterfly-theme-background-color: @butterfly-theme-color-base;
// (各图形框)普通情况背景色
@butterfly-theme-color: fade(@butterfly-theme-color-base, 80%);
// 主题色
@butterfly-primary-color: @butterfly-primary-color-base;
// 普通情况下字体颜色
@butterfly-normal-font-color: @butterfly-normal-font-color-base;
// 主题背景色上的字体颜色及icon颜色
@butterfly-overlay-font-color: @butterfly-overlay-font-color-base;
// 拖动时的背景色
@butterfly-box-move-background-color: fade(@butterfly-primary-color, 20%);
// 边框的样式
@butterfly-box-border: 1px solid @butterfly-box-border-color-base;
@butterfly-box-shadow: @butterfly-box-shadow-base;
@butterfly-box-radius: @butterfly-box-radius-base;
@butterfly-box-node-border-hover: 1px solid @butterfly-primary-color;
// 线条样式
@butterfly-line-color: @butterfly-line-color-base;
.iot-node {
position: absolute;
width: 110px;
height: 64px;
.title {
width: 100%;
height: 100%;
line-height: 64px;
text-align: center;
background: @butterfly-primary-color;
border-radius: 0 0 8.04px 8.04px;
}
/* .content {
height: 32px;
padding: 0 8px;
color: @butterfly-normal-font-color;
font-size: 12px;
line-height: 32px;
text-align: center;
background: @butterfly-theme-color;
border: @butterfly-box-border;
border-radius: 0 0 8.04px 8.04px;
}*/
.expand-btn {
position: absolute;
bottom: -17px;
left: 46px;
width: 20px;
height: 10px;
line-height: 8px;
text-align: center;
background: @butterfly-theme-color;
border: @butterfly-box-border;
border-radius: 4px;
box-shadow: @butterfly-box-shadow;
cursor: pointer;
}
.expand-btn:hover {
border: @butterfly-box-node-border-hover;
border-radius: 5px;
}
}
.compact-box-tree-page {
width: 100%;
height: 100%;
.compact-box-tree-canvas {
width: 100%;
height: 100%;
}
.butterfly-wrapper {
position: relative;
}
.text-box {
position: absolute;
top: -45px;
left: 0;
display: inline-block;
width: 100%;
height: 30px;
padding: 0 10px;
font-size: 12px;
line-height: 30px;
text-align: center;
// background-color: pink;
}
.label {
position: absolute;
display: inline-block;
width: 80px;
height: 28px;
padding: 4px;
color: @butterfly-primary-color;
text-align: center;
//background: @butterfly-theme-color;
}
}
</style>
node.js
import $ from 'jquery';
import { TreeNode } from 'butterfly-dag';
//import dd from './../../../assets/imgs/kai.png';
class BaseNode extends TreeNode {
draw = opts => {
let container = $('<div class="iot-node"></div>')
.css('top', opts.top + 'px')
.css('left', opts.left + 'px')
.attr('id', opts.id);
let titleDom = $(
// `<div class="title ${opts.options.color}">${opts.options.title}<div>`,
`<div class="title"><img src= "${require(`@/assets/imgs/kai.png`)}" width='32' height='32'/><div>`,
);
// let contentDom = $(`<div class="content">${opts.options.content}<div>`);
container.append(titleDom);
// container.append(contentDom);
this.showExpandBtn(container);
this.createText(container);
return container[0];
};
showExpandBtn(container = this.dom) {
let expandBtn = $(`<div class='expand-btn'>···</div>`);
expandBtn.on('click', e => {
e.stopPropagation();
e.preventDefault();
if (this.collapsed) {
// 可以在这里向后端请求数据,把node穿进去expand里面
this.expand();
} else {
this.collapse();
}
});
expandBtn.appendTo(container);
}
createText(container = this.dom) {
if (this.options.label) {
$('<span class="text-box"></span>').text(this.options.label).appendTo(container);
}
}
}
export default BaseNode;
edge.js
import $ from 'jquery';
import { Edge } from 'butterfly-dag';
class BaseEdge extends Edge {
draw(obj) {
let path = super.draw(obj);
if (this.options.color) {
$(path).addClass(this.options.color);
}
return path;
}
drawArrow(isShow) {
let dom = super.drawArrow(isShow);
if (this.options.color) {
$(dom).addClass(this.options.color);
}
return dom;
}
drawLabel(text) {
let dom = null;
if (text) {
dom = $(`<span class="label">${text}</span>`)[0];
}
return dom;
}
}
export default BaseEdge;
config.ts
import Node from './node.js';
import BaseEdge from './edge';
export const mockData: any = {
nodes: {
// 节点信息
isRoot: true,
id: 'Root',
title: '根节点',
content: 'root',
// iconClass: 'icon-class',
// iconType: 'icon-shujuji',
src: 'kai.png',
Class: Node,
// label: '节点上的描述',
endpoints: [
{
id: '1',
orientation: [0, 1],
pos: [0.5, 0],
},
],
children: [
{
id: 'subNode1',
Class: Node,
title: '子节点 1',
content: 'sub node 1',
// collapsed: true,
//iconType: 'icon-guize-kai',
// iconClass: 'icon-class',
label: '4011',
endpoints: [
{
id: '1',
orientation: [0, -1],
pos: [0.5, 0],
},
{
id: '2',
orientation: [0, 1],
pos: [0.5, 0],
},
],
children: [
{
id: 'subNode1-1',
Class: Node,
title: '子节点 1-1',
content: 'sub node 1-1',
//iconType: 'icon-guize-kai',
// iconClass: 'icon-class',
label: '4011',
endpoints: [
{
id: '1',
orientation: [0, -1],
pos: [0.5, 0],
},
{
id: '2',
orientation: [0, 1],
pos: [0.5, 0],
},
],
},
{
id: 'subNode1-2',
Class: Node,
title: '子节点 1-2',
content: 'sub node 1-2',
// iconType: 'icon-guize-kai',
// iconClass: 'icon-class',
label: '4011',
endpoints: [
{
id: '1',
orientation: [0, -1],
pos: [0.5, 0],
},
{
id: '2',
orientation: [0, 1],
pos: [0.5, 0],
},
],
},
],
},
{
id: 'subNode2',
Class: Node,
title: '子节点 2',
content: 'sub node 2',
// iconType: 'icon-guize-kai',
// iconClass: 'icon-class',
// collapsed: true,
label: '4011',
endpoints: [
{
id: '1',
orientation: [0, -1],
pos: [0.5, 0],
},
{
id: '2',
orientation: [0, 1],
pos: [0.5, 0],
},
],
children: [
{
id: 'subNode2-1',
Class: Node,
title: '子节点 2-1',
content: 'sub node 2-1',
// iconType: 'icon-guize-kai',
// iconClass: 'icon-class',
label: '4011',
endpoints: [
{
id: '1',
orientation: [0, -1],
pos: [0.5, 0],
},
{
id: '2',
orientation: [0, 1],
pos: [0.5, 0],
},
],
},
{
id: 'subNode2-2',
Class: Node,
title: '子节点 2-2',
content: 'sub node 2-2',
// iconType: 'icon-guize-kai',
// iconClass: 'icon-class',
label: '4011',
endpoints: [
{
id: '1',
orientation: [0, -1],
pos: [0.5, 0],
},
{
id: '2',
orientation: [0, 1],
pos: [0.5, 0],
},
],
},
],
},
{
id: 'subNode3',
Class: Node,
label: '4011',
title: '子节点 3',
content: 'sub node 3',
// iconType: 'icon-guize-kai',
// iconClass: 'icon-class',
endpoints: [
{
id: '1',
orientation: [0, -1],
pos: [0.5, 0],
},
{
id: '2',
orientation: [0, 1],
pos: [0.5, 0],
},
],
},
],
},
edges: [
// 连线信息
{
id: '0',
source: '1',
target: '1',
sourceNode: 'Root',
targetNode: 'subNode1',
type: 'endpoint',
// label: 'line描述',
// labelPosition: 0.9,
//arrowPosition: 0.7, // 箭头的位置
//labelOffset: 20,
Class: BaseEdge,
},
{
id: '1',
source: '1',
target: '1',
sourceNode: 'Root',
targetNode: 'subNode2',
type: 'endpoint',
// label: 'line描述',
// labelPosition: 0.9,
//labelOffset: 20,
Class: BaseEdge,
},
{
id: '2',
source: '1',
target: '1',
sourceNode: 'Root',
targetNode: 'subNode3',
type: 'endpoint',
// label: 'line描述',
//labelPosition: 0.9,
// labelOffset: 20,
Class: BaseEdge,
},
{
id: '3',
source: '2',
target: '1',
sourceNode: 'subNode1',
targetNode: 'subNode1-1',
type: 'endpoint',
// label: 'line描述',
Class: BaseEdge,
},
{
id: '4',
source: '2',
target: '1',
sourceNode: 'subNode1',
targetNode: 'subNode1-2',
type: 'endpoint',
// label: 'line描述',
Class: BaseEdge,
},
{
id: '5',
source: '2',
target: '1',
sourceNode: 'subNode2',
targetNode: 'subNode2-1',
type: 'endpoint',
// label: 'line描述',
Class: BaseEdge,
},
{
id: '6',
source: '2',
target: '1',
sourceNode: 'subNode2',
targetNode: 'subNode2-2',
type: 'endpoint',
// label: 'line描述',
Class: BaseEdge,
},
],
};
