基于butterfly库来实现流程图的开发

2023-11-08 11:00

本文主要是介绍基于butterfly库来实现流程图的开发,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

butterfly官方文档

butterfly官方示例

这篇文章吧是去年(2022年)写的,当时花了挺多时间做了个下面的这个demo,但是去年一直没有使用。今年刚好有一个项目需要用到,就从头开始搞吧,但是下面这个demo有些复杂,所以又相当于重新踩了一边坑,当然也更加加深了印象。
因此打算把这篇文章重新整理一下,会给出一个简单demo,说一下开发的流程以及注意事项。
在这里插入图片描述
删除功能也做了,是鼠标右键弹出菜单的格式,上面没有演示,右键菜单也支持扩展。

关于上面这个demo感兴趣的可以关注我的公众号,回复关键词 butterfly付款,进行付款,不多一块一。将付款截图在公众号里发给我,我在公众号里发你下载地址。生活困难,希望大家能够理解。12个小时内一定会回复。

下载后 npm install 下载依赖, npm run dev启动项目

开始

demo效果图
在这里插入图片描述

安装

npm install butterfly-dag@4.2.1
npm install jquery@3.6.0

建议安装上面这两个版本

先定义点的样子

我是习惯先写结点
节点类:node.ts

import $ from 'jquery'
import { Node } from 'butterfly-dag'
import './node.scss'//定义锚点结构
interface baseEndpoint {//锚点的唯一标识id: string//锚点的位置orientation: Array<number>
}// 线条
interface EdgeI {//自身节点的idsourceNode: string//自身节点锚点的起始idsource: string//目标节点的idtargetNode: string//目标节点的锚点结束idtarget: string//类型type: string
}// 节点结构
interface NodeI {// 节点idnodeId: string// 节点类型,默认矩形nodeType: 'rectangle-node'//节点显示的名称nodeLabel: string//节点对应的值nodeValue: string | number// 节点位置nodeLeft: numbernodeTop: number//锚点endpoints: Array<baseEndpoint>//子级children: Array<NodeI>//前置节点beforeNode: Array<string>//后置节点afterNode: Array<string>Class: any//父级idparentId?: string
}class BaseNode extends Node {//节点配置options: NodeIid: stringtop: numberleft: numberconstructor(opts: NodeI) {super(opts)this.options = optsthis.id = opts.nodeIdthis.top = opts.nodeTopthis.left = opts.nodeLeft}//创建节点draw = () => {//如果文本内容过长进行截取const desc =this.options.nodeLabel?.length > 5? `${this.options.nodeLabel.substring(0, 5)}...`: this.options.nodeLabelconst nodeDom = $(`<div class="${this.options.nodeType}">${desc}</div>`,).css('top', this.options.nodeTop).css('left', this.options.nodeLeft).attr('id', this.options.nodeId).attr('value', this.options.nodeValue).addClass(this.options.nodeType)if (this.options.nodeLabel?.length > 5) {nodeDom.attr('title', this.options.nodeLabel)}console.log('节点是:', nodeDom[0])//返回当前节点的dom对象return nodeDom[0]}
}export default BaseNode
export type { EdgeI, NodeI }

我这里是因为实际需要写了几个接口来规范数据结构,这个看个人的实际需求。

注意点:

1、 你可以自定义节点内包含哪些内容,比如上面的 NodeI,但是在你重新的类里,一定要在构造函数里对必填值进行赋值

 //节点配置options: NodeIid: stringtop: numberleft: numberconstructor(opts: NodeI) {super(opts)this.options = optsthis.id = opts.nodeIdthis.top = opts.nodeTopthis.left = opts.nodeLeft}

idtopleft 是必填值,id是唯一表示,topleft都是用于定位节点在什么位置的。具体可以看官方文档,关于节点的部分,只要你满足节点需要的参数就可以,我这里是为了避免属性冲突另外定义的。

2、draw 方法名不要改,这是用来生成节点的。该方法最后会返回要生成的dom元素,你可以在dom上挂载你想要的元素。节点长什么样子,完全看你想让他什么样

3、最好一定要把你这个类导出来,后面很有用

export default BaseNode

节点样式:node.scss

.rectangle-node{width: 120px;height: 40px;border: 1px solid #D7D7D7;border-radius: 3px;color: #000;text-align: center;line-height: 40px;position: absolute;cursor:pointer;
}

这块没什么好说的就是用来修饰你上面的dom元素的,但是要注意节点必须要设置成绝对定位

position: absolute;

在画布上生成节点

<template><div class="flow-edit-chart" id="flow-edit-chart" />
</template><script lang="ts" setup>
import { onMounted, ref } from 'vue'
//引入butterfly
import { Canvas } from 'butterfly-dag'
import 'butterfly-dag/dist/index.css'
// 引入节点类、连线类型
import { EdgeI, NodeI } from '../butterfly/node'
import BaseNode from '../butterfly/node'//画布
const canvas = ref()const nodeList:Array<NodeI> = [{nodeId: 'A',nodeType: 'rectangle-node',nodeLabel: 'A',nodeValue: 'A',nodeLeft: 100,nodeTop: 100,endpoints: [{id: 'right',orientation: [1, 0],},{id: 'left',orientation: [-1, 0],},],children: [],beforeNode: [],afterNode: [],Class: BaseNode,},{nodeId: 'B',nodeType: 'rectangle-node',nodeLabel: 'B',nodeValue: 'B',nodeLeft: 300,nodeTop: 100,endpoints: [{id: 'right',orientation: [1, 0],},{id: 'left',orientation: [-1, 0],},],children: [],beforeNode: [],afterNode: [],Class: BaseNode,},{nodeId: 'C',nodeType: 'rectangle-node',nodeLabel: 'C',nodeValue: 'C',nodeLeft: 500,nodeTop: 100,endpoints: [{id: 'right',orientation: [1, 0],},{id: 'left',orientation: [-1, 0],},],children: [],beforeNode: [],afterNode: [],Class: BaseNode,},
]const edgeList:Array<EdgeI> = [{sourceNode: 'A',targetNode: 'B',source: 'right',target: 'left',type: 'endpoint',},{sourceNode: 'B',targetNode: 'C',source: 'right',target: 'left',type: 'endpoint',},
]onMounted(() => {// 获取绘制容器let dom = document.getElementById('flow-edit-chart')//生成画布canvas.value = new Canvas({root: dom, //canvas的根节点(必传)zoomable: true, //可缩放(可传)moveable: true, //可平移(可传)draggable: true, //节点可拖动(可传)linkable: true, //节点可连线theme: {//主题edge: {shapeType: 'Bezier',arrow: true,},},})if (canvas.value) {console.log('canvas:', canvas.value)//单独生成点和线// canvas.value.addNodes(nodeList)// canvas.value.addEdges(edgeList)//,一起生成点和线canvas.value.draw({ nodes: nodeList, edges: edgeList })}
})// 新增节点
const addNode = () => {const reactangle: NodeI = {nodeId: `${new Date().getTime()}`,nodeType: 'rectangle-node',nodeLabel: `${new Date().getTime()}`,nodeValue: `${new Date().getTime()}`,nodeLeft: 500,nodeTop: 100,endpoints: [{id: 'right',orientation: [1, 0],},{id: 'left',orientation: [-1, 0],},],children: [],beforeNode: [],afterNode: [],Class: BaseNode,}console.log('新增的节点:', reactangle)canvas.value.addNode(reactangle)
}
</script><style lang="scss" scoped>
.flow-edit-chart {width: 100%;height: 300px;margin-bottom: 10px;
}
</style>

注意点:
1、节点的数据格式,以我的为例。因为我是定义了节点的数据格式的,所以我的数据要与定义的格式一致;如果没有定义,那么要按照官方节点格式来,一般只需要id top left

{nodeId: 'A',nodeType: 'rectangle-node',nodeLabel: 'A',nodeValue: 'A',nodeLeft: 100,nodeTop: 100,endpoints: [{id: 'right',orientation: [1, 0],},{id: 'left',orientation: [-1, 0],},],children: [],beforeNode: [],afterNode: [],Class: BaseNode,},

2、这点很重要,你的节点数据里必须指明类,比如: Class: BaseNode,这个BaseNode就是我们上面导出的节点类。这句话的意思是,这个节点要按照我定义的这个样式来,要按照我的格式来生成dom元素
3、锚点:endpoints,如下图,一般就是上下左右四个点。在一个节点里,锚点的id不可以重复;在不同的节点里,锚点可以重复。orientation: [1, 0] 表示锚点在右面, orientation: [-1, 0]表示锚点在左边,依次类推
在这里插入图片描述
4、线条,以下面的代码为例,参数代表什么含义上面代码里有备注就不说了。下面代码的作用就是从开始节点Aright锚点开始连线,连接到目标节点Bleft锚点上。

{sourceNode: 'A',targetNode: 'B',source: 'right',target: 'left',type: 'endpoint',},

在这里插入图片描述
5、生成节点和线

// 方式1,可以在点和线都存在的时候用,比如显示流程图
canvas.value.draw({ nodes: nodeList, edges: edgeList })// 方式2.生成点和线有各自的方法
// 比如以上面为例,我将b节点删除后,我想让a自动连接到c上就可以使用canvas.value.addNodes(nodeList)
canvas.value.addEdges(edgeList)

常用API学习

这里就简单学习一下实际工作中比较常用的,其他的自行查看官方API

画布(Canvas)

这里只记录常用的属性,其他内容自行查看官方API

root <dom> (必填)
实例容器,一般是一个具有宽高的dom元素, canvas 根节点(必传)

zoomable <Boolean> (选填)
画布是否可缩放;值类型 boolean,默认 false

moveable <Boolean> (选填)
画布是否可移动;值类型 boolean,默认 false

draggable <Boolean> (选填)
画布节点是否可拖动;值类型 boolean,默认 false

linkable <Boolean> (选填)
画布锚点是否可以拖动连线;值类型 boolean,默认 false

disLinkable <Boolean> (选填)
画布锚点是否可以拖动断开线;值类型 boolean,默认 false

layout <Object> (选填)
画布初始化根据设置的布局来自动排版

theme
画布主题配置,默认初始化样式和交互,内容有点多,自行查看官方API

画布API

常用API方法
canvas.draw (data, calllback)
作用:画布的渲染方法, 注意画布渲染是异步渲染

canvas.redraw (data, calllback)
作用:重新渲染方法,会将之前的所有元素删除重新渲染, 注意画布渲染是异步渲染

canvas.getDataMap (data, calllback)
作用:获取画布的所有数据:节点,线段,分组

canvas.setLinkable (boolean)
作用:设置画布所有节点是否可拉线

canvas.setDisLinkable (boolean)
作用:设置画布所有节点是否可断线

canvas.setDraggable (boolean)
作用:设置画布所有节点是否可拖动

canvas.getGroup (string)
作用:根据id获取group

canvas.addGroup (object|Group, nodes, options)
作用:添加分组。若分组不存在,则创建分组并把nodes放进分组内;若分组存在,则会把nodes放进当前分组内。

canvas.removeGroup (string | Group)
作用:删除节点组, 但不会删除里面的节点

canvas.getNode (string)
作用:根据id获取node

canvas.addNode ( object | Node )
作用:添加节点

canvas.addNodes ( array< object | Node > )
作用:批量添加节点

canvas.removeNode (string)
作用:根据id删除节点

canvas.removeNodes (array)
作用:批量删除节点

canvas.addEdge (object|Edge)
作用:添加连线

canvas.addEdges (array<object|Edge>)
作用:批量添加连线

canvas.removeEdge (param)
作用:根据id或者Edge对象来删除线

canvas.removeEdges (param)
作用:根据id或者Edge对象来批量删除线

canvas.getNeighborEdges (string)
作用:根据node id获取相邻的edge

canvas.setEdgeZIndex (edges, zIndex)
作用:设置线段z-index属性

canvas.setZoomable (boolean, boolean)
作用:设置画布缩放

画布事件

let canvas = new Canvas({...});
canvas.on('type key', (data) => {//data 数据
});

参数key值:

  • system.canvas.click 点击画布空白处
  • system.canvas.zoom 画布缩放
  • system.nodes.delete 删除节点
  • system.node.move 移动节点
  • system.node.click 点击节点
  • system.nodes.add 批量节点添加
  • system.links.delete 删除连线
  • system.link.connect 连线成功
  • system.link.reconnect 线段重连
  • system.link.click 线段点击事件
  • system.group.add 新增节点组
  • system.group.delete 删除节点组
  • system.group.move 移动节点组
  • system.group.addMembers 节点组添加节点
  • system.group.removeMembers 节点组删除节点
  • system.endpoint.limit 锚点连接数超过上限
  • system.multiple.select 框选结束
  • system.drag.start 拖动开始
  • system.drag.move 拖动
  • system.drag.end 拖动结束

画布辅助事件

canvas.setGridMode (show, options)
作用:设置网格背景


this.canvas.setGridMode(true, {isAdsorb: false,         // 是否自动吸附,默认关闭theme: {shapeType: 'line',     // 展示的类型,支持line & circlegap: 23,               // 网格间隙adsorbGap: 8,          // 吸附间距background: '#fff',     // 网格背景颜色lineColor: '#000',     // 网格线条颜色lineWidth: 1,          // 网格粗细circleRadiu: 1,        // 圆点半径circleColor: '#000'    // 圆点颜色}
});

canvas.setGuideLine (show, options)
作用:设置辅助线

this.canvas.setGuideLine(true, {limit: 1,             // 限制辅助线条数adsorp: {enable: false       // 开启吸附效果gap: 5              // 吸附间隔},theme: {lineColor: 'red',   // 网格线条颜色lineWidth: 1,       // 网格粗细}
});

canvas.save2img (options)
作用:画布保存为图片

canvas.updateRootResize ()
作用:当root移动或者大小发生变化时需要更新位置

注:
使用了一下网格和辅助线(可能是写的有问题),辅助线没有生效;网格第一次加载会很慢,其次就是缩放时,网格不会缩放。这里我加了一个缩放监听,来动态改变网格的大小,但是网格线会越来越多

如果想要网格背景的话,可以通过css来实现。

节点组(Group)

这个目前用不到,可以自行查看官方文档

节点(Node)

用法

const Node = require('butterfly-dag').Node;// 当canvas为TreeCanvas时可选TreeNode
// const TreeNode = require('butterfly-dag').TreeNode;
class ANode extends Node {draw(obj) {// 这里可以根据业务需要,自己生成dom}
}// 初始化画布渲染
canvas.draw({nodes: [{id: 'xxxx',top: 100,left: 100,Class: ANode //设置基类之后,画布会根据自定义的类来渲染// 参考下面属性...}]
})// 动态添加
canvas.addNode({id: 'xxx',top: 100,left: 100,Class: ANode// 参考下面属性...
});

节点常用属性

id <String> (必填)
节点唯一标识

top <Number> (必填)
y轴坐标: 节点所在画布的坐标;若在节点组中,则是相对于节点组内部的坐标

left <Number> (必填)
x轴坐标: 节点所在画布的坐标;若在节点组中,则是相对于节点组内部的坐标

draggable <Boolean> (选填)
设置该节点是否能拖动:为可覆盖全局的draggable属性

group <String> (选填)
父级group的id: 设置后该节点会添加到节点组中

endpoints <Array> (选填)
系统锚点配置: 当有此配置会加上系统的锚点

Class <Class> (选填)
拓展类:当传入拓展类的时候,该节点则会按拓展类的draw方法进行渲染,拓展类的相关方法也会覆盖父类的方法

scope <Boolean> (选填)
作用域:当scope一致的节点才能拖动进入节点组

自定义属性
可以自定义属性,然后结合拓展类可以自定义节点的样式和内容

import {Node} from 'butterfly-dag';
import $ from 'jquery';
//自定义的节点样式
import './node.scss';class BaseNode extends Node {constructor(opts) {super(opts);this.id = opts.id;this.top = opts.y;this.left = opts.x;this.options = opts;}draw = (opts) => {let container = $('<div class="fruchterman-node"></div>').css('top', this.top + 'px').css('left', this.left + 'px').attr('id', this.id = opts.id);container.text(opts.options.label);return container[0];}
}export default BaseNode;

节点外部调用API

node.getWidth ()
作用: 获取节点宽度

node.removeEndpoint(string)
作用:节点中删除锚点

node.getEndpoint (id, type)
作用:获取节点中的锚点

node.moveTo (x, y)
作用: 节点移动坐标的方法

node.remove ()
作用: 节点删除的方法。与canvas.removeNode的方法作用一致。

node.emit (event, data)
作用: 节点发送事件的方法,画布及任何一个元素都可接收。

[树状布局]treeNode.collapseNode (string)
作用: 树状节点的节点收缩功能

[树状布局]treeNode.expandNode (string)
作用: 树状节点的节点展开功能

线(Edge)

let edges = [{source: '0',target: '1'
}];

线属性

type <String> (选填)
标志线条连接到节点还是连接到锚点。默认值为endpoint

// endpoint类型线段: 锚点连接锚点的线段
{type: 'endpoint',sourceNode: '', //连接源节点idsource: '',     //连接源锚点idtargetNode: '', //连接目标节点idtarget: ''      //连接目标锚点id
}
// node类型线段: 节点连接节点的线段
{type: 'node',source: '',     //连接源节点idtarget: ''      //连接目标节点id
}

shapeType <String> (选填)
线条的类型: Bezier/Flow/Straight/Manhattan/AdvancedBezier/Bezier2-1/Bezier2-2/Bezier2-3/BrokenLine
在这里插入图片描述

label <String/Dom> (选填)
线条上注释: 可传字符串和dom

labelPosition <Number> (选填)
线条上注释的位置: 取值0-1之间, 0代表代表在线段开始处,1代表在线段结束处。 默认值0.5

arrow <Boolean> (选填)
是否加箭头配置: 默认false

arrowPosition <Number> (选填)
箭头位置: 取值0-1之间, 0代表代表在线段开始处,1代表在线段结束处。 默认值0.5

arrowShapeType <String> (选填)
箭头样式类型: 可使用系统集成的和可使用自己注册的,只需要保证类型对应即可。

// 自行注册的
import {Arrow} from 'butterfly-dag';
Arrow.registerArrow([{key: 'yourArrow1',type: 'svg',width: 10,   // 选填,默认8pxheight: 10,  // 选填,默认8pxcontent: require('/your_fold/your_arrow.svg') // 引用外部svg
}, {key: 'yourArrow1',type: 'pathString',content: 'M5 0 L0 -2 Q 1.0 0 0 2 Z' // path的d属性
}]);

线段外部API

edge.redraw ()
作用: 更新线段位置: 线段所在的节点或者锚点位置发生变化后, 需要调用下redraw更新其对应的线

edge.setZIndex (index)
作用: 设置线段的z-index值

edge.updateLabel (label)
作用: 更新线段的注释

edge.remove ()
作用: 线段删除的方法。与canvas.removeEdge的方法作用一致。

edge.emit(event,data)
作用: 线段发送事件的方法,画布及任何一个元素都可接收。

edge.on(event,callback)
作用: 线段接收事件的方法,能接收画布及任何一个元素的事件。

edge.addAnimate (options)
作用: 给该线段加上动画

锚点

用法

// 用法一:
canvas.draw({nodes: [{...endpoints: [{id: 'point_1',type: 'target',orientation: [-1, 0],pos: [0, 0.5]}]}]
})// 用法二: 此方法必须在node的mount挂载后才能使用
let node = this.canvas.getNode('xxx');
node.addEndpoint({id: 'xxxx',type: 'target',dom: dom           // 使用此属性用户可以使用任意的一个dom作为一个锚点
});

锚点属性

id <String> (必填)
节点唯一标识

orientation <Array>(选填)
方向: (1) 控制系统锚点方向 (2) 控制线段的出入口方向
下: [0,1]、上: [0,-1]、右: [1,0]、左: [-1,0]

pos <Array> (选填)
位置: 控制系统锚点位置。可配合orientation使用,控制系统锚点
取值: [0-1之间 , 0-1之间],0代表最左/上侧,1代表最右/下侧

type <String> (选填)
锚点类型:

  • source: 来源锚点。线段只出不入
  • target: 目标锚点。线段只入不出
  • undefined: 未定义锚点。线段能入能出,但取决于第一根连线是入还是出
  • onlyConnect: 不能拖动断开线的锚点。线段能入能出,但拖动断开线

scope <String> (选填)
作用域: 锚点之间scope相同才可以连线。

disLinkable <Boolean > (选填)
禁止锚点拖动断开线段

其他内容略,自行查看官方文档

示例

let nodes = [{id: '0',label: 'a',x: 100,y: 100,Class: NodeClass,endpoints: [{id: 'point_0',type: 'source',orientation: [1,0]}]},{id: '1',label: 'b',x: 200,y: 150,Class: NodeClass,endpoints: [{id: 'point_1',type: 'target',orientation: [-1,0]}]}
];
let edges = [{type: 'endpoint',sourceNode: '0',source: 'point_0',targetNode: '1',target: 'point_1',arrow: true,arrowPosition: 0.8}];

在这里插入图片描述

提示 & 菜单(tooltips & menu)

提示用法

import {Tips} from 'butterfly-dag';
let container = document.getElementById('.you-target-dom');
Tips.createTip({className: `butterfly-custom-tips`,targetDom: container,genTipDom: () => { return $('<div>内容</div>')[0] },placement: 'right'
});

菜单用法

import {Tips} from 'butterfly-dag';
let container = document.getElementById('.you-target-dom');
Tips.createMenu({className: `butterfly-custom-menu`,targetDom: container,genTipDom: () => { return $('<div>内容</div>')[0] },placement: 'right',action: 'click',closable: true
});

API
在这里插入图片描述

tip示例

canvas.draw({groups: [], // 分组信息nodes: nodes, // 节点信息edges: edges // 连线信息
},(data) => {console.log('渲染完成了:',data);let nodes = data.nodes;nodes.forEach(item => {Tips.createTip({targetDom: item.dom,genTipDom: () => { return $(`<div>${item.options.label}</div>`)[0]; },placement: 'right'});});
});

在这里插入图片描述

布局(layout)

自行查看官方文档

这篇关于基于butterfly库来实现流程图的开发的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/369397

相关文章

使用DeepSeek API 结合VSCode提升开发效率

《使用DeepSeekAPI结合VSCode提升开发效率》:本文主要介绍DeepSeekAPI与VisualStudioCode(VSCode)结合使用,以提升软件开发效率,具有一定的参考价值... 目录引言准备工作安装必要的 VSCode 扩展配置 DeepSeek API1. 创建 API 请求文件2.

Java中使用Java Mail实现邮件服务功能示例

《Java中使用JavaMail实现邮件服务功能示例》:本文主要介绍Java中使用JavaMail实现邮件服务功能的相关资料,文章还提供了一个发送邮件的示例代码,包括创建参数类、邮件类和执行结... 目录前言一、历史背景二编程、pom依赖三、API说明(一)Session (会话)(二)Message编程客

Java中List转Map的几种具体实现方式和特点

《Java中List转Map的几种具体实现方式和特点》:本文主要介绍几种常用的List转Map的方式,包括使用for循环遍历、Java8StreamAPI、ApacheCommonsCollect... 目录前言1、使用for循环遍历:2、Java8 Stream API:3、Apache Commons

C#提取PDF表单数据的实现流程

《C#提取PDF表单数据的实现流程》PDF表单是一种常见的数据收集工具,广泛应用于调查问卷、业务合同等场景,凭借出色的跨平台兼容性和标准化特点,PDF表单在各行各业中得到了广泛应用,本文将探讨如何使用... 目录引言使用工具C# 提取多个PDF表单域的数据C# 提取特定PDF表单域的数据引言PDF表单是一

使用Python实现高效的端口扫描器

《使用Python实现高效的端口扫描器》在网络安全领域,端口扫描是一项基本而重要的技能,通过端口扫描,可以发现目标主机上开放的服务和端口,这对于安全评估、渗透测试等有着不可忽视的作用,本文将介绍如何使... 目录1. 端口扫描的基本原理2. 使用python实现端口扫描2.1 安装必要的库2.2 编写端口扫

PyCharm接入DeepSeek实现AI编程的操作流程

《PyCharm接入DeepSeek实现AI编程的操作流程》DeepSeek是一家专注于人工智能技术研发的公司,致力于开发高性能、低成本的AI模型,接下来,我们把DeepSeek接入到PyCharm中... 目录引言效果演示创建API key在PyCharm中下载Continue插件配置Continue引言

MySQL分表自动化创建的实现方案

《MySQL分表自动化创建的实现方案》在数据库应用场景中,随着数据量的不断增长,单表存储数据可能会面临性能瓶颈,例如查询、插入、更新等操作的效率会逐渐降低,分表是一种有效的优化策略,它将数据分散存储在... 目录一、项目目的二、实现过程(一)mysql 事件调度器结合存储过程方式1. 开启事件调度器2. 创

使用Python实现操作mongodb详解

《使用Python实现操作mongodb详解》这篇文章主要为大家详细介绍了使用Python实现操作mongodb的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、示例二、常用指令三、遇到的问题一、示例from pymongo import MongoClientf

SQL Server使用SELECT INTO实现表备份的代码示例

《SQLServer使用SELECTINTO实现表备份的代码示例》在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误,在SQLServer中,可以使用SELECTINT... 在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误。在 SQL Server 中,可以使用 SE

基于Go语言实现一个压测工具

《基于Go语言实现一个压测工具》这篇文章主要为大家详细介绍了基于Go语言实现一个简单的压测工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录整体架构通用数据处理模块Http请求响应数据处理Curl参数解析处理客户端模块Http客户端处理Grpc客户端处理Websocket客户端