npm install @antv/x6-plugin-selection@2.1.7 --save
npm install @antv/x6-vue-shape@2.0.11 --save
npm install @antv/x6@2.9.7 --save
npm i --save @vue/composition-api@1.7.2
index.vue
<template>
<!-- <div>
<div id="oneNode"></div>
</div> -->
<div id="coverCot" style="width: 100%; height: 500px;">
<div class="section-cot" style="width: 100%; height: 100%">
<div id="container" @click.stop="hideFn">
<MenuBar
v-if="showContextMenu"
ref="menuBar"
@callBack="contextMenuFn"
/>
<header>
<el-tooltip
class="item"
effect="dark"
content="放大"
placement="bottom"
>
<i class="el-icon-zoom-in" @click="zoomFn(0.2)" />
</el-tooltip>
<el-tooltip
class="item"
effect="dark"
content="缩小"
placement="bottom"
>
<i class="el-icon-zoom-out" @click="zoomFn(-0.2)" />
</el-tooltip>
<el-tooltip
class="item"
effect="dark"
content="适应屏幕"
placement="bottom"
>
<i class="el-icon-full-screen" @click="centerFn" />
</el-tooltip>
<el-tooltip
class="item"
effect="dark"
content="执行"
placement="bottom"
>
<i class="el-icon-video-play" @click="startFn()" />
</el-tooltip>
<el-tooltip
class="item"
effect="dark"
content="保存"
placement="bottom"
>
<i class="el-icon-upload" @click="saveFn()" />
</el-tooltip>
<el-tooltip
class="item"
effect="dark"
content="加载保存页面"
placement="bottom"
>
<i class="el-icon-link" @click="loadFn()" />
</el-tooltip>
</header>
<div id="oneNode" />
</div>
</div>
<!-- <DialogCondition ref="dialogCondition"></DialogCondition> -->
<DialogMysql ref="dialogMysql"></DialogMysql>
</div>
</template>
<script>
// import { Graph } from '@antv/x6';
import { register } from '@antv/x6-vue-shape';
import { Graph, Node, Path, Edge, Platform, StringExt } from '@antv/x6'
import { Selection } from '@antv/x6-plugin-selection'
import DagNode from './components/dag';
import MenuBar from "./components/menuBar";
// import DialogCondition from "./components/dialog/condition.vue";
import DialogMysql from "./components/dialog/mysql.vue";
let graph = null;
export default {
components: { MenuBar, DialogMysql },
data() {
return {
showContextMenu: false,
nodeData:{
"nodes": [
{
"id": "node-0",
"shape": "data-processing-dag-node",
"x": 0,
"y": 100,
"ports": [
{
"id": "node-0-out",
"group": "out"
}
],
"data": {
"name": "数据输入_1",
"type": "INPUT",
"checkStatus": "sucess"
}
},
{
"id": "node-1",
"shape": "data-processing-dag-node",
"x": 250,
"y": 100,
"ports": [
{
"id": "node-1-in",
"group": "in"
},
{
"id": "node-1-out",
"group": "out"
}
],
"data": {
"name": "数据筛选_1",
"type": "FILTER"
}
},
{
"id": "node-2",
"shape": "data-processing-dag-node",
"x": 250,
"y": 200,
"ports": [
{
"id": "node-2-out",
"group": "out"
}
],
"data": {
"name": "数据输入_2",
"type": "INPUT"
}
},
{
"id": "node-3",
"shape": "data-processing-dag-node",
"x": 500,
"y": 100,
"ports": [
{
"id": "node-3-in",
"group": "in"
},
{
"id": "node-3-out",
"group": "out"
}
],
"data": {
"name": "数据连接_1",
"type": "JOIN"
}
},
{
"id": "node-4",
"shape": "data-processing-dag-node",
"x": 750,
"y": 100,
"ports": [
{
"id": "node-4-in",
"group": "in"
}
],
"data": {
"name": "数据输出_1",
"type": "OUTPUT"
}
}
],
"edges": [
{
"id": "edge-0",
"source": {
"cell": "node-0",
"port": "node-0-out"
},
"target": {
"cell": "node-1",
"port": "node-1-in"
},
"shape": "data-processing-curve",
"zIndex": -1,
"data": {
"source": "node-0",
"target": "node-1"
}
},
{
"id": "edge-1",
"source": {
"cell": "node-2",
"port": "node-2-out"
},
"target": {
"cell": "node-3",
"port": "node-3-in"
},
"shape": "data-processing-curve",
"zIndex": -1,
"data": {
"source": "node-2",
"target": "node-3"
}
},
{
"id": "edge-2",
"source": {
"cell": "node-1",
"port": "node-1-out"
},
"target": {
"cell": "node-3",
"port": "node-3-in"
},
"shape": "data-processing-curve",
"zIndex": -1,
"data": {
"source": "node-1",
"target": "node-3"
}
},
{
"id": "edge-3",
"source": {
"cell": "node-3",
"port": "node-3-out"
},
"target": {
"cell": "node-4",
"port": "node-4-in"
},
"shape": "data-processing-curve",
"zIndex": -1,
"data": {
"source": "node-3",
"target": "node-4"
}
}
]
},
// 元素校验状态
CellStatus: {
DEFAULT:'default',
SUCCESS:'success',
ERROR:'error',
},
// 节点状态列表
nodeStatusList: [
{
id: 'node-0',
status: 'success',
},
{
id: 'node-1',
status: 'success',
},
{
id: 'node-2',
status: 'success',
},
{
id: 'node-3',
status: 'success',
},
{
id: 'node-4',
status: 'error',
statusMsg: '错误信息示例',
},
],
// 边状态列表
edgeStatusList: [
{
id: 'edge-0',
status: 'success',
},
{
id: 'edge-1',
status: 'success',
},
{
id: 'edge-2',
status: 'success',
},
{
id: 'edge-3',
status: 'success',
},
],
};
},
mounted() {
// 1. 注册节点
register({
// shape: 'data-processing-dag-node',
// width: 212,
// height: 48,
// component: DagNode,
shape: 'data-processing-dag-node',
width: 212,
height: 48,
component: DagNode,
// port默认不可见
ports: {
groups: {
in: {
position: 'left',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: 'transparent',
strokeWidth: 1,
fill: 'transparent',
},
},
},
out: {
position: {
name: 'right',
args: {
dx: -32,
},
},
attrs: {
circle: {
r: 4,
magnet: true,
stroke: 'transparent',
strokeWidth: 1,
fill: 'transparent',
},
},
},
},
},
});
// 注册连线
Graph.registerConnector(
'curveConnector',
(sourcePoint, targetPoint) => {
const hgap = Math.abs(targetPoint.x - sourcePoint.x)
const path = new Path()
path.appendSegment(
Path.createSegment('M', sourcePoint.x - 4, sourcePoint.y),
)
path.appendSegment(
Path.createSegment('L', sourcePoint.x + 12, sourcePoint.y),
)
// 水平三阶贝塞尔曲线
path.appendSegment(
Path.createSegment(
'C',
sourcePoint.x < targetPoint.x
? sourcePoint.x + hgap / 2
: sourcePoint.x - hgap / 2,
sourcePoint.y,
sourcePoint.x < targetPoint.x
? targetPoint.x - hgap / 2
: targetPoint.x + hgap / 2,
targetPoint.y,
targetPoint.x - 6,
targetPoint.y,
),
)
path.appendSegment(
Path.createSegment('L', targetPoint.x + 2, targetPoint.y),
)
return path.serialize()
},
true,
)
Edge.config({
markup: [
{
tagName: 'path',
selector: 'wrap',
attrs: {
fill: 'none',
cursor: 'pointer',
stroke: 'transparent',
strokeLinecap: 'round',
},
},
{
tagName: 'path',
selector: 'line',
attrs: {
fill: 'none',
pointerEvents: 'none',
},
},
],
connector: { name: 'curveConnector' },
attrs: {
wrap: {
connection: true,
strokeWidth: 10,
strokeLinejoin: 'round',
},
line: {
connection: true,
stroke: '#A2B1C3',
strokeWidth: 1,
targetMarker: {
name: 'classic',
size: 6,
},
},
},
})
Graph.registerEdge('data-processing-curve', Edge, true)
// 2. 创建画布
graph = new Graph({
// grid: {
// size: 10,
// visible: true,
// type: "dot", // 'dot' | 'fixedDot' | 'mesh'
// args: {
// color: "#a05410", // 网格线/点颜色
// thickness: 1, // 网格线宽度/网格点大小
// },
// },
background: {
color: "#fffbe6", // 设置画布背景颜色
},
container: document.getElementById('oneNode'),
// width: 1000,
// height: 1000,
// container: document.getElementById('container'),
panning: {
enabled: true,
eventTypes: ['leftMouseDown', 'mouseWheel'],
},
mousewheel: {
enabled: true,
modifiers: 'ctrl',
factor: 1.1,
maxScale: 1.5,
minScale: 0.5,
},
highlighting: {
magnetAdsorbed: {
name: 'stroke',
args: {
attrs: {
fill: '#fff',
stroke: '#31d0c6',
strokeWidth: 4,
},
},
},
},
connecting: {
snap: true,
allowBlank: false,
allowLoop: false,
highlight: true,
sourceAnchor: {
name: 'left',
args: {
dx: Platform.IS_SAFARI ? 4 : 8,
},
},
targetAnchor: {
name: 'right',
args: {
dx: Platform.IS_SAFARI ? 4 : -8,
},
},
createEdge() {
return graph.createEdge({
shape: 'data-processing-curve',
attrs: {
line: {
strokeDasharray: '5 5',
},
},
zIndex: -1,
})
},
// 连接桩校验
validateConnection({ sourceMagnet, targetMagnet }) {
// 只能从输出链接桩创建连接
if (!sourceMagnet || sourceMagnet.getAttribute('port-group') === 'in') {
return false
}
// 只能连接到输入链接桩
if (!targetMagnet || targetMagnet.getAttribute('port-group') !== 'in') {
return false
}
return true
},
},
});
graph.use(
new Selection({
multiple: true,
rubberEdge: true,
rubberNode: true,
modifiers: 'shift',
rubberband: true,
}),
)
// 单击节点获取节点信息
graph.on('node:click', (e) => {
const nodeItem = e.cell // 获取被点击的节点元素对象
console.log('单击', nodeItem)
})
//右键点击事件显示配置数据和删除弹窗
graph.on("node:contextmenu", ({ e, x, y, node, view }) => {
console.log(node,"===============node");
this.showContextMenu = true;
this.$nextTick(() => {
// this.$refs.menuBar.setItem({ type: 'node', item: node })
const p = graph.localToPage(x, y);
// this.$refs.menuBar.initFn(p.x, p.y, { type: "node", item: node });
console.log(x, y,"========x, y")
this.$refs.menuBar.initFn(x+80, y, { type: "node", item: node });
});
});
// // 3. 根据json数据创建节点,此处只取第一个
// let map = {};
// map.nodes = dagMap.nodes;
// graph.fromJSON(map);
// // 4. 设置节点状态
// let { id, status, statusMsg } = this.nodeStatusList[0];
// let node = graph.getCellById(id);
// let data = node.getData();
// node.setData({
// ...data,
// status,
// statusMsg,
// });
this.init(this.nodeData);
},
methods: {
getNodeById(id) {
return graph.getCellById(id);
},
hideFn() {
this.showContextMenu = false;
},
//右键弹窗删除
contextMenuFn(type, node) {
console.log(node,"===============2222222")
switch (type) {
case "remove":
if (node.type == "edge") {
graph.removeEdge(node.item.id);
} else if (node.type == "node") {
graph.removeNode(node.item.id);
}
break;
case "source":
this.$refs.dialogMysql.visible = true;
this.$refs.dialogMysql.init(node);
break;
}
this.showContextMenu = false;
},
//初始获取化工艺图
init(data){
graph.fromJSON(data)
const zoomOptions = {
padding: {
left: 10,
right: 10,
},
}
graph.zoomToFit(zoomOptions)
setTimeout(() => {
this.excuteAnimate()
}, 2000)
setTimeout(() => {
this.showNodeStatus()
this.stopAnimate()
}, 3000)
},
// 显示节点状态
showNodeStatus(){
this.nodeStatusList.forEach((item) => {
const { id, status, statusMsg } = item
const node = graph.getCellById(id)
// const data = node.getData() as CellStatus
// name: "数据输出_1"
// type: "OUTPUT"
const data = node.getData()
node.setData({
...data,
status,
statusMsg,
})
})
},
// 开启边的运行动画
excuteAnimate() {
graph.getEdges().forEach((edge) => {
edge.attr({
line: {
stroke: '#3471F9',
},
})
edge.attr('line/strokeDasharray', 5)
edge.attr('line/style/animation', 'running-line 30s infinite linear')
})
},
// 关闭边的动画
stopAnimate() {
graph.getEdges().forEach((edge) => {
edge.attr('line/strokeDasharray', 0)
edge.attr('line/style/animation', '')
})
this.edgeStatusList.forEach((item) => {
const { id, status } = item
const edge = graph.getCellById(id)
if (status === 'success') {
edge.attr('line/stroke', '#52c41a')
}
if (status === 'error') {
edge.attr('line/stroke', '#ff4d4f')
}
})
// 默认选中一个节点
graph.select('node-2')
},
//执行
startFn(item) {
console.log(item,"=========item")
this.init(item || this.nodeData);
graph.centerContent();
},
//放大缩小
zoomFn(num) {
graph.zoom(num);
},
//屏幕自适应
centerFn() {
const num = 1 - graph.zoom();
num > 1 ? graph.zoom(num * -1) : graph.zoom(num);
graph.centerContent();
},
// 保存
saveFn() {
localStorage.setItem(
"x6Json",
JSON.stringify(graph.toJSON({ diff: true }))
);
console.log(graph.toJSON({ diff: true }))
},
//图表加载保存数据
loadFn() {
this.timer && clearTimeout(this.timer);
const x6Json = JSON.parse(localStorage.getItem("x6Json"));
this.startFn(x6Json.cells);
},
},
};
</script>
<style scoped>
header {
display: flex;
justify-content: flex-end;
width: 100%;
height: 50px;
box-sizing: border-box;
}
header i {
margin: 8px;
font-size: 30px;
}
.section-cot {
display: flex;
}
.section-cot #container {
position: relative;
flex: 1;
}
.section-cot #container #oneNode {
width: 100%;
height: 450px;
}
</style>
components menuBar.vue
<template>
<el-card
class="box-card"
:style="{ left: x + 'px', top: y + 'px' }"
:stop="11"
@click.stop=""
style="padding: 0"
>
<div
class="text item"
@click.stop="callBack('source')"
v-if="item.type !== 'edge'"
>
配置数据源
</div>
<div class="text item" @click.stop="callBack('remove')">删除</div>
</el-card>
</template>
<script>
export default {
name: "MenuBar",
data() {
return {
x: "",
y: "",
item: {},
};
},
mounted() {},
methods: {
initFn(x, y, item) {
this.x = parseInt(x) + "";
this.y = y + "";
if (item) {
this.item = item;
}
},
setItem(item) {
this.item = item;
},
callBack(type) {
this.$emit("callBack", type, this.item);
},
},
};
</script>
<style scoped>
.box-card {
position: absolute;
z-index: 112;
}
.box-card ::v-deep .el-card__body {
padding: 0;
}
.text {
font-size: 14px;
}
.item {
padding: 10px 0;
text-align: center;
}
.item:hover {
color: #ffffff;
background-color: #409eff;
}
.box-card {
width: 150px;
}
</style>
dag.vue
<template>
<div class="data-processing-dag-node">
<div
class="main-area"
@mouseenter="onMainMouseEnter"
@mouseleave="onMainMouseLeave"
>
<div class="main-info">
<!-- 节点类型icon -->
<i
class="node-logo"
:style="{
backgroundImage: 'url(' + global.NODE_TYPE_LOGO[type] + ')',
}"
/>
<el-tooltip :content="name" placement="top" :open-delay="800">
<div class="ellipsis-row node-name">{{ name }}</div>
</el-tooltip>
</div>
<!-- 节点状态信息 -->
<div class="status-action">
<el-tooltip
:content="statusMsg"
v-if="status === global.CellStatus.ERROR"
placement="top"
>
<i class="status-icon status-icon-error" />
</el-tooltip>
<i
class="status-icon status-icon-success"
v-if="status === global.CellStatus.SUCCESS"
/>
<!-- 节点操作菜单 -->
<div class="more-action-container">
<i class="more-action" />
</div>
</div>
</div>
<!-- +号菜单 -->
<div class="plus-dag" v-if="type !== global.NodeType.OUTPUT">
<el-dropdown trigger="click">
<i class="plus-action" />
<!-- <i class="el-icon-circle-plus-outline el-icon--right"></i> -->
<el-dropdown-menu
slot="dropdown"
placement="bottom"
class="processing-node-menu"
>
<el-dropdown-item
v-for="(item, index) in global.PROCESSING_TYPE_LIST"
:key="index"
>
<div
class="node-dropdown-item"
@click="clickPlusDragMenu(item.type)"
>
<i
class="node-mini-logo"
:style="{
backgroundImage: `url(${global.NODE_TYPE_LOGO[item.type]})`,
}"
/>
{{ item.name }}
</div>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</template>
<script>
// import dagDictionary from './dagDictionary';
export default {
inject: ['getNode'],
data() {
return {
name: '',
status: '',
statusMsg: '',
type: '',
global: {
// 节点类型
NodeType: {
INPUT: 'INPUT', // 数据输入
FILTER: 'FILTER', // 数据过滤
JOIN: 'JOIN', // 数据连接
UNION: 'UNION', // 数据合并
AGG:'AGG', // 数据聚合
OUTPUT:'OUTPUT', // 数据输出
},
// 元素校验状态
CellStatus: {
DEFAULT:'default',
SUCCESS:'success',
ERROR:'error',
},
// 节点位置信息
Position: {
x: 0,
y: 100
},
// 加工类型列表
PROCESSING_TYPE_LIST: [
{
type: 'FILTER',
name: '数据筛选',
},
{
type: 'JOIN',
name: '数据连接',
},
{
type: 'UNION',
name: '数据合并',
},
{
type: 'AGG',
name: '数据聚合',
},
{
type: 'OUTPUT',
name: '数据输出',
},
],
// 不同节点类型的icon
NODE_TYPE_LOGO: {
INPUT:
'https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*RXnuTpQ22xkAAAAAAAAAAAAADtOHAQ/original', // 数据输入
FILTER:
'https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*ZJ6qToit8P4AAAAAAAAAAAAADtOHAQ/original', // 数据筛选
JOIN: 'https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*EHqyQoDeBvIAAAAAAAAAAAAADtOHAQ/original', // 数据连接
UNION:
'https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*k4eyRaXv8gsAAAAAAAAAAAAADtOHAQ/original', // 数据合并
AGG: 'https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*TKG8R6nfYiAAAAAAAAAAAAAADtOHAQ/original', // 数据聚合
OUTPUT:
'https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*zUgORbGg1HIAAAAAAAAAAAAADtOHAQ/original', // 数据输出
},
}
};
},
mounted() {
let node = this.getNode();
// 初始化数据绑定
this.mapper(node.data, this.$data);
// 节点数据变化监听,从而绑定数据
node.on('change:data', ({ current }) => this.mapper(current, this.$data));
},
methods: {
mapper(source, target) {
for (let key in target) {
target[key] = source[key] ?? target[key];
}
},
// 鼠标进入矩形主区域的时候显示连接桩
onMainMouseEnter() {
let node = this.getNode();
// 获取该节点下的所有连接桩
const ports = node.getPorts() || [];
ports.forEach((port) => {
node.setPortProp(port.id, 'attrs/circle', {
fill: '#fff',
stroke: '#85A5FF',
});
});
},
// 鼠标离开矩形主区域的时候隐藏连接桩
onMainMouseLeave() {
let node = this.getNode();
// 获取该节点下的所有连接桩
const ports = node.getPorts() || [];
ports.forEach((port) => {
node.setPortProp(port.id, 'attrs/circle', {
fill: 'transparent',
stroke: 'transparent',
});
});
},
// 点击添加下游+号
clickPlusDragMenu(type) {
this.createDownstream(type);
// this.setState({
// plusActionSelected: false,
// });
},
// // 添加下游菜单的打开状态变化
// onPlusDropdownOpenChange (value){
// this.setState({
// plusActionSelected: value,
// })
// },
// 创建下游的节点和边
createDownstream(type) {
let node = this.getNode();
const { graph } = node.model || {};
if (graph) {
// 获取下游节点的初始位置信息
const position = this.getDownstreamNodePosition(node, graph);
// 创建下游节点
const newNode = this.createNode(type, graph, position);
const source = node.id;
const target = newNode.id;
// 创建该节点出发到下游节点的边
this.createEdge(source, target, graph);
}
},
/**
* 创建边并添加到画布
* @param source
* @param target
* @param graph
*/
createEdge(source, target, graph) {
const edge = {
id: this.uuid(),
shape: 'data-processing-curve',
source: {
cell: source,
port: `${source}-out`,
},
target: {
cell: target,
port: `${target}-in`,
},
zIndex: -1,
data: {
source,
target,
},
};
if (graph) {
graph.addEdge(edge);
}
},
/**
* 创建节点并添加到画布
* @param type 节点类型
* @param graph
* @param position 节点位置
* @returns
*/
createNode(type, graph, position) {
if (!graph) {
return {};
}
let newNode = {};
const sameTypeNodes = graph
.getNodes()
.filter((item) => item.getData()?.type === type);
const typeName = this.global.PROCESSING_TYPE_LIST?.find(
(item) => item.type === type
)?.name;
const id = this.uuid();
const node = {
id,
shape: 'data-processing-dag-node',
x: position?.x,
y: position?.y,
ports: this.getPortsByType(type, id),
data: {
name: `${typeName}_${sameTypeNodes.length + 1}`,
type,
},
};
newNode = graph.addNode(node);
return newNode;
},
/**
* 根据起点初始下游节点的位置信息
* @param node 起始节点
* @param graph
* @returns
*/
getDownstreamNodePosition(node, graph, dx = 250, dy = 100) {
// 找出画布中以该起始节点为起点的相关边的终点id集合
const downstreamNodeIdList = [];
graph.getEdges().forEach((edge) => {
const originEdge = edge.toJSON()?.data;
if (originEdge.source === node.id) {
downstreamNodeIdList.push(originEdge.target);
}
});
// 获取起点的位置信息
const position = node.getPosition();
let minX = Infinity;
let maxY = -Infinity;
graph.getNodes().forEach((graphNode) => {
if (downstreamNodeIdList.indexOf(graphNode.id) > -1) {
const nodePosition = graphNode.getPosition();
// 找到所有节点中最左侧的节点的x坐标
if (nodePosition.x < minX) {
minX = nodePosition.x;
}
// 找到所有节点中最x下方的节点的y坐标
if (nodePosition.y > maxY) {
maxY = nodePosition.y;
}
}
});
return {
x: minX !== Infinity ? minX : position.x + dx,
y: maxY !== -Infinity ? maxY + dy : position.y,
};
},
// 根据节点的类型获取ports
getPortsByType(type, nodeId) {
let ports = [];
switch (type) {
case this.global.NodeType.INPUT:
ports = [
{
id: `${nodeId}-out`,
group: 'out',
},
];
break;
case this.global.NodeType.OUTPUT:
ports = [
{
id: `${nodeId}-in`,
group: 'in',
},
];
break;
default:
ports = [
{
id: `${nodeId}-in`,
group: 'in',
},
{
id: `${nodeId}-out`,
group: 'out',
},
];
break;
}
return ports;
},
uuid() {
var s = [];
var hexDigits = '0123456789abcdef';
for (var i = 0; i < 32; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = '4'; // bits 12-15 of the time_hi_and_version field to 0010
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
s[8] = s[13] = s[18] = s[23];
var uuid = s.join('');
return uuid;
},
},
// computed: {
// global: function () {
// return dagDictionary;
// },
// },
};
</script>
<style lang="less" scoped>
.data-processing-dag-node {
display: flex;
flex-direction: row;
align-items: center;
}
.main-area {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 12px;
width: 180px;
height: 48px;
color: rgba(0, 0, 0, 65%);
font-size: 12px;
font-family: PingFangSC;
line-height: 24px;
background-color: #fff;
box-shadow: 0 -1px 4px 0 rgba(209, 209, 209, 50%), 1px 1px 4px 0 rgba(217, 217, 217, 50%);
border-radius: 2px;
border: 1px solid transparent;
}
.main-area:hover {
border: 1px solid rgba(0, 0, 0, 10%);
box-shadow: 0 -2px 4px 0 rgba(209, 209, 209, 50%), 2px 2px 4px 0 rgba(217, 217, 217, 50%);
}
.node-logo {
display: inline-block;
width: 24px;
height: 24px;
background-repeat: no-repeat;
background-position: center;
background-size: 100%;
}
.node-name {
overflow: hidden;
display: inline-block;
width: 70px;
margin-left: 6px;
color: rgba(0, 0, 0, 65%);
font-size: 12px;
font-family: PingFangSC;
white-space: nowrap;
text-overflow: ellipsis;
vertical-align: top;
}
.status-action {
display: flex;
flex-direction: row;
align-items: center;
}
.status-icon {
display: inline-block;
width: 24px;
height: 24px;
}
.status-icon-error {
background: url('https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*SEISQ6My-HoAAAAAAAAAAAAAARQnAQ')
no-repeat center center / 100% 100%;
}
.status-icon-success {
background: url('https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*6l60T6h8TTQAAAAAAAAAAAAAARQnAQ')
no-repeat center center / 100% 100%;
}
.more-action-container {
margin-left: 12px;
width: 15px;
height: 15px;
text-align: center;
cursor: pointer;
}
.more-action {
display: inline-block;
width: 3px;
height: 15px;
background: url('https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*tFw7SIy-ttQAAAAAAAAAAAAADtOHAQ/original')
no-repeat center center / 100% 100%;
}
.plus-dag {
visibility: hidden;
position: relative;
margin-left: 12px;
height: 48px;
}
.plus-action {
position: absolute;
top: calc(50% - 8px);
left: 0;
width: 16px;
height: 16px;
background: url('https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*ScX2R4ODfokAAAAAAAAAAAAADtOHAQ/original')
no-repeat center center / 100% 100%;
cursor: pointer;
}
.plus-action:hover {
background-image: url('https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*tRaoS5XhsuQAAAAAAAAAAAAADtOHAQ/original');
}
.plus-action:active,
.plus-action-selected {
background-image: url('https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*k9cnSaSmlw4AAAAAAAAAAAAADtOHAQ/original');
}
.x6-node-selected .main-area {
border-color: #3471f9;
}
.x6-node-selected .plus-dag {
visibility: visible;
}
.processing-node-menu {
padding: 2px 0;
width: 118px!important;
background-color: #fff;
box-shadow: 0 9px 28px 8px rgba(0, 0, 0, 5%), 0 6px 16px 0 rgba(0, 0, 0, 8%),
0 3px 6px -4px rgba(0, 0, 0, 12%);
border-radius: 2px;
}
.processing-node-menu ul {
margin: 0;
padding: 0;
}
.processing-node-menu li {
list-style:none;
}
.each-sub-menu {
padding: 6px 12px;
width: 100%;
}
.each-sub-menu:hover {
background-color: rgba(0, 0, 0, 4%);
}
.each-sub-menu a {
display: inline-block;
width: 100%;
height: 16px;
font-family: PingFangSC;
font-weight: 400;
font-size: 12px;
color: rgba(0, 0, 0, 65%);
}
.each-sub-menu span {
margin-left: 8px;
vertical-align: top;
}
.each-disabled-sub-menu a {
cursor: not-allowed;
color: rgba(0, 0, 0, 35%);
}
.node-mini-logo {
display: inline-block;
width: 16px;
height: 16px;
background-repeat: no-repeat;
background-position: center;
background-size: 100%;
vertical-align: top;
}
@keyframes running-line {
to {
stroke-dashoffset: -1000;
}
}
</style>
dialog mysql.vue
<template>
<el-dialog
:title="node.item ? node.item.data.label : ''"
:visible.sync="visible"
width="600px"
>
<el-form :inline="true" class="demo-form-inline">
<el-form-item label="名字11">
<el-input v-model="label" placeholder="修改名字"></el-input>
</el-form-item>
</el-form>
<footer class="footer">
<el-button type="primary" @click="submit">确定</el-button>
</footer>
</el-dialog>
</template>
<script>
export default {
name: "dialogMysql",
data() {
return {
visible: false,
bool: true,
node: {},
label: "",
};
},
mounted() {},
methods: {
init(item) {
this.node = item;
this.label = item.item.data.name;
},
submit() {
var node = this.$parent.getNodeById(this.node.item.id);
node.setData(
Object.assign({}, this.node.item.data, { name: this.label })
);
// this.node.setData({})
// this.node.item.data.label = this.label;
this.visible = false;
},
},
};
</script>
<style scoped>
section {
display: flex;
align-items: center;
justify-content: center;
}
.footer {
margin-top: 15px;
display: flex;
align-items: center;
justify-content: center;
}
</style>