BPMN.JS中文教程学习

基础篇

vue+ bpmn.js

建模BpmnModeler+将数据转图形bpmnModeler.importXML

// basic.vue<script>// 引入相关的依赖import BpmnModeler from 'bpmn-js/lib/Modeler'import {xmlStr} from '../mock/xmlStr' // 这里是直接引用了xml字符串export default {name: '',components: {},// 生命周期 - 创建完成(可以访问当前this实例)created() { },// 生命周期 - 载入后, Vue 实例挂载到实际的 DOM 操作完成,一般在该过程进行 Ajax 交互mounted() {this.init()},data() {return {// bpmn建模器bpmnModeler: null,container: null,canvas: null}},methods: {init() {// 获取到属性ref为“canvas”的dom节点const canvas = this.$refs.canvas// 建模this.bpmnModeler = new BpmnModeler({container: canvas})this.createNewDiagram()},createNewDiagram() {// 将字符串转换成图显示出来this.bpmnModeler.importXML(xmlStr, (err) => {if (err) {// console.error(err)} else {// 这里是成功之后的回调, 可以在这里做一系列事情this.success()}})},success() {// console.log('创建成功!')}}}</script>

创建BpmnModeler实例时配置additionalModules属性

左侧工具栏 prpertiesProviderModule

右侧属性栏 bpmn-js-properties-panel

// panel.vue
<script>
...
import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda'
...
methods: {init() {// 获取到属性ref为“canvas”的dom节点const canvas = this.$refs.canvas// 建模this.bpmnModeler = new BpmnModeler({container: canvas,//添加控制板propertiesPanel: {parent: '#js-properties-panel'},additionalModules: [// 左边工具栏以及节点propertiesProviderModule,// 右边的工具栏propertiesPanelModule]})this.createNewDiagram()},createNewDiagram() {// 将字符串转换成图显示出来this.bpmnModeler.importXML(xmlStr, (err) => {if (err) {// console.error(err)} else {// 这里是成功之后的回调, 可以在这里做一系列事情this.success()}})},success() {// console.log('创建成功!')}}

http篇

字符串数据通过请求获取

// axios.vue
<script>
...
import axios from 'axios'
import { xmlStr } from '../mock/xmlStr' // 引入一个本地的xml字符串, 若是没有获取到后台的数据则用它
​
export default {...data () {return {...loading: true,xmlUrl: '',defaultXmlStr: xmlStr}},methods: {async init () {this.loading = truethis.xmlUrl = await this.getXmlUrl()console.log(this.xmlUrl)this.loading = falsethis.$nextTick(() => { // 等待 DOM 更新之后再对工作流进行初始化this.initBpmn()})},getXmlUrl () { // 该方法模拟请求后台获取bpmn文件地址return new Promise(resolve => {setTimeout(() => {const url = 'https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmnMock.bpmn' // 模拟网络请求的一个地址resolve(url)}, 1000)})},initBpmn () {... // 这里是初始化工作流的代码this.createNewDiagram()},async createNewDiagram () {const that = thislet bpmnXmlStr = ''if (this.xmlUrl === '') { // 若是后台没有数据则使用默认的一个xmlbpmnXmlStr = this.defaultXmlStrthis.transformCanvas(bpmnXmlStr)} else {let res = await axios({method: 'get',timeout: 120000,url: that.xmlUrl,headers: { 'Content-Type': 'multipart/form-data' }})console.log(res)bpmnXmlStr = res['data']this.transformCanvas(bpmnXmlStr)}},transformCanvas(bpmnXmlStr) {// 将字符串转换成图显示出来this.bpmnModeler.importXML(bpmnXmlStr, (err) => {if (err) {console.error(err)} else {this.success()}// 让图能自适应屏幕var canvas = this.bpmnModeler.get('canvas')canvas.zoom('fit-viewport')})},success () {console.log('创建成功!')}}
}
</script>

将编辑之后的最新bpmn发送给后台

该功能就涉及到了bpmn.js中的事件绑定, 也就是前端需要给图形绑定一个事件来检测到图形的改变, 并获取到最新的xml 信息.

获取最新xml信息

bpmnModeler.on('commandStack.changed',function(){})

// save.vue
<script>success () {console.log('创建成功!')this.addBpmnListener()},// 添加绑定事件addBpmnListener () {const that = this// 给图绑定事件,当图有发生改变就会触发这个事件this.bpmnModeler.on('commandStack.changed', function () {that.saveDiagram(function(err, xml) {console.log(xml) // 这里获取到的就是最新的xml信息})})},// 下载为bpmn格式,done是个函数,调用的时候传入的saveDiagram(done) {// 把传入的done再传给bpmn原型的saveXML函数调用this.bpmnModeler.saveXML({ format: true }, function(err, xml) {done(err, xml)})}
</script>
​

这段代码是一个Vue组件中的一部分,用于处理保存操作。首先定义了一个名为success的方法,当保存成功时调用。在success方法中调用了addBpmnListener方法,用于添加BPMN图的事件监听器

addBpmnListener方法中,通过**this.bpmnModeler.on**方法给BPMN图绑定了一个事件监听器,当图有发生改变时触发**commandStack.changed**事件。事件回调函数中调用了saveDiagram方法,并传入一个回调函数作为参数。

saveDiagram方法用于保存BPMN图为XML格式,并通过传入的回调函数将保存结果传递出去。在方法内部调用了this.bpmnModeler.saveXML方法来保存XML,并将结果通过传入的回调函数返回。

总的来说,这段代码的逻辑是:当BPMN图有变化时,触发保存操作,并将保存的XML结果传递出去。在保存操作完成后,可以通过传入的回调函数获取保存的XML信息。

编辑完保存为bpmn文件或svg文件

API

bpmnModeler.saveSVG(function(){})

this.bpmnModeler.saveXML( function(err, xml) {callback(err, xml)} )

encodeURIComponent li标签

// save.vue
<script>...addBpmnListener () {const that = this// 获取a标签dom节点const downloadLink = this.$refs.saveDiagramconst downloadSvgLink = this.$refs.saveSvg// 给图绑定事件,当图有发生改变就会触发这个事件this.bpmnModeler.on('commandStack.changed', function () {that.saveSVG(function(err, svg) {that.setEncoded(downloadSvgLink, 'diagram.svg', err ? null : svg)})that.saveDiagram(function(err, xml) {that.setEncoded(downloadLink, 'diagram.bpmn', err ? null : xml)})})},// 下载为SVG格式,done是个函数,调用的时候传入的saveSVG(done) {// 把传入的done再传给bpmn原型的saveSVG函数调用this.bpmnModeler.saveSVG(done)},// 下载为bpmn格式,done是个函数,调用的时候传入的saveDiagram(done) {// 把传入的done再传给bpmn原型的saveXML函数调用this.bpmnModeler.saveXML({ format: true }, function(err, xml) {done(err, xml)})},// 当图发生改变的时候会调用这个函数,这个data就是图的xmlsetEncoded(link, name, data) {// 把xml转换为URI,下载要用到的const encodedData = encodeURIComponent(data)// 下载图的具体操作,改变a的属性,className令a标签可点击,href令能下载,download是下载的文件的名字console.log(link, name, data)let xmlFile = new File([data], 'test.bpmn')console.log(xmlFile)if (data) {link.className = 'active'link.href = 'data:application/bpmn20-xml;charset=UTF-8,' + encodedDatalink.download = name}}
</script>

将最新的XML保存至后端(待补充)

事件篇

监听modeler并绑定事件

shape.added 新增一个shape之后触发;

shape.move.end 移动完一个shape之后触发;

shape.removed 删除一个shape之后触发;

API

var elementRegistry = bpmnjs.get('elementRegistry') var shape = e.element ? elementRegistry.get(e.element.id) : e.shape

// event.vue
<script>
...
success () {this.addModelerListener()
},
// 监听 modeler
addModelerListener() {const bpmnjs = this.bpmnModelerconst that = this// 这里我是用了一个forEach给modeler上添加要绑定的事件const events = ['shape.added', 'shape.move.end', 'shape.removed', 'connect.end',  'connect.move']events.forEach(function(event) {that.bpmnModeler.on(event, e => {console.log(event, e)var elementRegistry = bpmnjs.get('elementRegistry')var shape = e.element ? elementRegistry.get(e.element.id) : e.shapeconsole.log(shape)})})
},

监听element并绑定事件

上面介绍的是监听modeler并绑定事件, 可能你也需要监听用户点击图形上的element或者监听某个element改变:

element.click 点击元素;

element.changed 当元素发生改变的时候(包括新增、移动、删除元素)

// event.vue
<script>
...
success () {...this.addEventBusListener()
},
addEventBusListener () {let that = thisconst eventBus = this.bpmnModeler.get('eventBus') // 需要使用eventBusconst eventTypes = ['element.click', 'element.changed'] // 需要监听的事件集合eventTypes.forEach(function(eventType) {eventBus.on(eventType, function(e) {console.log(e)})})
}
</script>
//获取Elememnt
eventBus.on(eventType, function(e) {if (!e || e.element.type == 'bpmn:Process') return // 这里我的根元素是bpmn:Processconsole.log(e)var elementRegistry = this.bpmnModeler.get('elementRegistry')var shape = elementRegistry.get(e.element.id) // 传递id进去console.log(shape) // {Shape}console.log(e.element) // {Shape}console.log(JSON.stringify(shape)===JSON.stringify(e.element)) // true
})

通过监听事件判断操作方式

上面我们已经介绍了modelerelement的监听绑定方式, 在事件应用中, 你更多的需要知道用户要进行什么操作, 好写对应的业务逻辑.

这里我就以我工作中要用到的场景为案例进行讲解.

新增了shape

新增了线(connection)

删除了shape和connection

移动了shape和线

// event.vue...success () {this.addModelerListener()this.addEventBusListener()},// 添加绑定事件addBpmnListener () {const that = this// 获取a标签dom节点const downloadLink = this.$refs.saveDiagramconst downloadSvgLink = this.$refs.saveSvg// 给图绑定事件,当图有发生改变就会触发这个事件this.bpmnModeler.on('commandStack.changed', function () {that.saveSVG(function(err, svg) {that.setEncoded(downloadSvgLink, 'diagram.svg', err ? null : svg)})that.saveDiagram(function(err, xml) {that.setEncoded(downloadLink, 'diagram.bpmn', err ? null : xml)})})},addModelerListener() {// 监听 modelerconst bpmnjs = this.bpmnModelerconst that = this// 'shape.removed', 'connect.end', 'connect.move'const events = ['shape.added', 'shape.move.end', 'shape.removed']events.forEach(function(event) {that.bpmnModeler.on(event, e => {var elementRegistry = bpmnjs.get('elementRegistry')var shape = e.element ? elementRegistry.get(e.element.id) : e.shape// console.log(shape)if (event === 'shape.added') {console.log('新增了shape')} else if (event === 'shape.move.end') {console.log('移动了shape')} else if (event === 'shape.removed') {console.log('删除了shape')}})})},addEventBusListener() {// 监听 elementlet that = thisconst eventBus = this.bpmnModeler.get('eventBus')const eventTypes = ['element.click', 'element.changed']eventTypes.forEach(function(eventType) {eventBus.on(eventType, function(e) {if (!e || e.element.type == 'bpmn:Process') returnif (eventType === 'element.changed') {that.elementChanged(eventType, e)} else if (eventType === 'element.click') {console.log('点击了element')}})})},elementChanged(eventType, e) {var shape = this.getShape(e.element.id)if (!shape) {// 若是shape为null则表示删除, 无论是shape还是connect删除都调用此处console.log('无效的shape')// 由于上面已经用 shape.removed 检测了shape的删除, 因此这里只判断是否是线if (this.isSequenceFlow(shape.type)) {console.log('删除了线')}}if (!this.isInvalid(shape.type)) {if (this.isSequenceFlow(shape.type)) {console.log('改变了线')}}},getShape(id) {var elementRegistry = this.bpmnModeler.get('elementRegistry')return elementRegistry.get(id)},isInvalid (param) { // 判断是否是无效的值return param === null || param === undefined || param === ''},isSequenceFlow (type) { // 判断是否是线return type === 'bpmn:SequenceFlow'}

自定义Palette篇(左侧绘画板)

原型

$inject

自定义Renderer篇(画布上元素)

自定义ContextPad篇

编辑、删除节点篇

封装组件

Properties篇

我们在用bpmn.js画的每一个节点其实都被称之为diagram element(图表元素, 是不是很好理解😁)

而在bpmn文件中的每个xml标签称之为BPMN element.

diagram elementBPMN element的一些属性关联起来靠的是一个叫做businessObject的属性. 从名称上理解你也可以知道它是一个对象(Object), 你可以在这个对象中添加上一些特殊的属性, 并且这些属性是可以直接插到BPMN element上的.

读取
var elementRegistry = bpmnJs.get('elementRegistry').get('ID').businessObject
//一致element情况
element.bussinessObject
const {bussinessObject}=element
修改
var moddle = bpmnJS.get('moddle');
​
// 创建一个BPMN element , 并且载入到导出的xml里
var newCondition = moddle.create('bpmn:FormalExpression', {body: '${ value > 100 }'
});
​
// 写入属性, 但是不支持撤销 
sequenceFlow.conditionExpression = newCondition;
//支撑撤销/重新 
var modeling = bpmnJS.get('modeling');
modeling.updateProperties(sequenceFlowElement, {conditionExpression: newCondition
});
​
//updateProperties()
//属性查看下面meta-model descriptor
modeling.updateProperties(startEventElement, {name: '我是修改后的虚线节点',isInterrupting: false
})
​

《meta-model descriptor》

XML Schema(XML Schema Definition,XSD)是一种用于描述 XML 文档结构和约束的语言。它本身是一种 XML 文档,用于定义 XML 文档中元素和属性的结构、数据类型以及约束规则。

XML Schema 的目的是为 XML 文档提供验证机制,以确保其符合特定的规范和要求。通过 XML Schema,您可以明确指定 XML 文档中包含的元素、属性以及它们之间的关系,指定数据类型,提供约束规则,以及定义文档的命名空间等。

XML Schema 是 XML 的一个重要标准,由 W3C(World Wide Web Consortium)制定和维护。它通常以 .xsd 文件扩展名保存,并可以使用各种 XML 解析器和编辑器进行创建、编辑和验证。

XML Schema 的基本结构包括:

  1. 元素声明(Element Declaration):定义 XML 文档中的元素,包括元素名称、类型、出现次数等信息。

  2. 属性声明(Attribute Declaration):定义 XML 元素的属性,包括属性名称、数据类型、默认值等信息。

  3. 简单类型(Simple Type):定义 XML 元素或属性的数据类型,如字符串、数字、日期等。

  4. 复杂类型(Complex Type):定义包含其他元素和属性的复杂结构,可以指定序列、选择和重复等约束规则。

通过定义这些结构,XML Schema 可以帮助确保 XML 文档的结构、内容和数据格式符合预期,从而提高文档的可靠性和互操作性。

Properties-panel篇(上)

// 这里引入的是右侧属性栏这个框
import propertiesPanelModule from 'bpmn-js-properties-panel'
// 而这个引入的是右侧属性栏里的内容
import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda'
​
additionalModules: [propertiesPanelModule,propertiesProviderModule
]

//第一个propertiesPanelModule 表示的是属性栏这个框, 就是告诉别人这里要有个属性栏; //第二个propertiesProviderModule表示的是属性栏里的内容, 也就是点击不同的element该显示什么内容.

自定义

既然这样的话, 我们只需要重写propertiesProviderModule就可以了, 不要引入官方提供的(也就是从bpmn-js-properties-panel/lib/provider/camunda引入的), 而是自定义一个**propertiesProviderModule**来显示自己想要的内容.

扩展使用Properties-panel properties-panel-extension

Properties-panel篇(下)

样式主题切换

自定义properties-panel

input框双向绑定 @change

/**
* 改变控件触发的事件
* @param { Object } input的Event
* @param { String } 要修改的属性的名称
*/
changeField (event, type) {const value = event.target.valuelet properties = {}properties[type] = valuethis.element[type] = valuethis.updateProperties(properties) // 调用属性更新方法
}

修改节点名称label属性

updateName(name) {const { modeler, element } = thisconst modeling = modeler.get('modeling')modeling.updateLabel(element, name)// 等同于 modeling.updateProperties(element, { name })
},
API:modeling.updateLabel()

修改节点颜色color属性

/*** 改变控件触发的事件* @param { Object } input的Event* @param { String } 要修改的属性的名称*/
changeField(event, type) {const value = event.target.valuelet properties = {}properties[type] = valueif (type === 'color') { // 若是color属性this.onChangeColor(value)}this.element[type] = valuethis.updateProperties(properties)
},
onChangeColor(color) {const { modeler, element } = thisconst modeling = this.modeler.get('modeling')modeling.setColor(element, {fill: color,stroke: null})
},
​
//API:modeling.setColor()

修改event节点类型

实现这个功能我们需要用到bpmnReplace.replaceElement这个方法.

首先让我们看看event里这个属性是放在哪里的.

changeEventType(event) { // 改变下拉框const { modeler, element } = thisconst value = event.target.valueconst bpmnReplace = modeler.get('bpmnReplace')this.eventType = valuebpmnReplace.replaceElement(element, {type: element.businessObject.$type,eventDefinitionType: value})
},

现在改变下拉框的值, 就可以改变eventDefinitionType的值了, 不过还有一个问题, 就是你点击了其它的节点, 然后再次点回开始节点的时候, 下拉框的默认值(应该是本身所选元素的eventDefinition属性值)就不对了, 也就是说我们还需要获取到这个开始节点本身的eventDefinitionType值.

init () {modeler.on('selection.changed', e => {this.selectedElements = e.newSelectionthis.element = e.newSelection[0]console.log(this.element)this.setDefaultProperties() // 设置一些默认的值})
}
setDefaultProperties() {const { element } = thisif (element) {const { type, businessObject } = elementif (this.verifyIsEvent(type)) { // 若是event类型// 获取默认的 eventDefinitionTypethis.eventType = businessObject.eventDefinitions ? businessObject.eventDefinitions[0]['$type'] : ''}}
}

修改Task节点的类型

changeTaskType(event) {const { modeler, element } = thisconst value = event.target.value // 当前下拉框选择的值const bpmnReplace = modeler.get('bpmnReplace')bpmnReplace.replaceElement(element, {type: value // 直接修改type就可以了})
}

初始化properties-panel并设置一些默认值

init () {modeler.on('selection.changed', e => {this.selectedElements = e.newSelectionthis.element = e.newSelection[0]console.log(this.element)this.setDefaultProperties() // 设置一些默认的值})
}
setDefaultProperties() {const { element } = thisif (element) {// 这里可以拿到当前点击的节点的所有属性const { type, businessObject } = element// doSomeThing}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/805839.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

三维点云:对原始点云数据进行体素化

文章目录 一、原始点云二、对原始点云进行体素化三、结果展示 一、原始点云 &#x1f349;原始点云为.pts文件&#xff0c;内容为x, y, z的坐标 原始点云展示 二、对原始点云进行体素化 使用open3d库实现&#xff0c;如果没有需要在命令行执行pip install open3d import o…

vue vue3 手写 动态加载组件

效果展示 一、需求背景&#xff1a; # vue3 项目涉及很多图表加载、表格加载 #考虑手写一个动态加载组件 二、实现思路 通过一个加载状态变量&#xff0c;通过v-if判断&#xff0c;加载状态的变量等于哪一个&#xff0c;动态加载组件内部就显示的哪一块组件。 三、实现效果…

安装Schedule库的方法最终解答!_Python第三方库

安装Python第三方库Schedule 我的环境&#xff1a;Window10&#xff0c;Python3.7&#xff0c;Anaconda3&#xff0c;Pycharm2023.1.3 Schedule库 Schedule 是一个轻量级、功能强大而灵活的任务调度工具库&#xff0c;用于在指定的时间间隔内执行任务。为用户提供了简单易用的…

kali使用msf+apkhook520+cploar实现安卓手的攻击

学习网络安全的过程中&#xff0c;突发奇想怎么才能控制或者说是攻击手机 边找工作边实验 话不多说启动kali 一、使用msfapktool生成简单的木马程序 首先使用kali自带的msfvenom写上这样一段代码 选择安卓 kali的ip 一个空闲的端口 要输出的文件名 msfvenom -p android/met…

kafka(五)——消费者流程分析(c++)

概念 ​ 消费者组&#xff08;Consumer Group&#xff09;&#xff1a;由多个consumer组成。消费者组内每个消费者负责消费不同分区的数据&#xff0c;一个分区只能由一个组内消费者消费&#xff1b;消费者组之间互不影响。所有的消费者都属于某个消费者组&#xff0c;即消费者…

今日arXiv最热大模型论文:Dataverse,针对大模型的开源ETL工具,数据清洗不再难!

引言&#xff1a;大数据时代下的ETL挑战 随着大数据时代的到来&#xff0c;数据处理的规模和复杂性不断增加&#xff0c;尤其是在大语言模型&#xff08;LLMs&#xff09;的开发中&#xff0c;对海量数据的需求呈指数级增长。这种所谓的“规模化法则”表明&#xff0c;LLM的性…

HTML - 请你说一下如何阻止a标签跳转

难度级别:初级及以上 提问概率:55% a标签的默认语义化功能就是超链接,HTML给它的定位就是与外部页面进行交流,不过也可以通过锚点功能,定位到本页面的固定id区域去。但在开发场景中,又避免不了禁用a标签的需求,那么都有哪些方式可以禁用…

【就近接入,智能DNS-Geo DNS ,大揭秘!】

做过后端服务或者网络加速的小伙伴&#xff0c;可能或多或少都听说过&#xff0c;智能DNS或者Geo DNS&#xff0c;就是根据用户的位置&#xff0c;返回离用户最近的服务节点&#xff0c;就近接入&#xff0c;以达到服务提速的效果。 那么大家有没想过&#xff0c;这个背后的原理…

C++:日期类的实现 const修饰 取地址及const取地址操作符重载(类的6个默认成员函数完结篇)

一、日期类的实现 根据之前赋值运算符重载逻辑&#xff0c;我们现在来实现完整的日期类。 1.1 判断小于 上篇博客已经实现: bool operator<(const Date& d) {if (_year < d._year){return true;}else if (_year d._year){if (_month < d._month){return true…

总结C/C++中程序内存区域划分

C/C程序内存分配的几个区域&#xff1a; 1. 栈区&#xff08;stack&#xff09;&#xff1a;在执行函数时&#xff0c;函数内局部变量的存储单元都可以在栈上创建&#xff0c;函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中&#xff0c;效率很⾼…

OpenHarmony开发技术:【国际化】实例

国际化 如今越来的越多的应用都走向了海外&#xff0c;应用走向海外需要支持不同国家的语言&#xff0c;这就意味着应用资源文件需要支持不同语言环境下的显示。本节就介绍一下设备语言环境变更后&#xff0c;如何让应用支持多语言。 应用支持多语言 ArkUI开发框架对多语言的…

TypeScript—详解、小案例(配合源代码)

简介&#xff1a;TypeScript是微软开发的 JavaScript 的超集&#xff0c;TypeScript兼容JavaScript&#xff0c;可以载入JavaScript代码然后运行。TypeScript与JavaScript相比进步的地方 包括&#xff1a;加入注释&#xff0c;让编译器理解所支持的对象和函数&#xff0c;编译器…

Web路径专题

文章目录 1.资源定位1.前置条件上下文路径设置 2.上下文路径介绍重点说明 3.资源定位方式资源路径 上下文路径 资源位置a.html定位C.java定位 4.浏览器和服务器解析的区别1.浏览器解析/&#xff08;地址变化&#xff09;2.服务器解析/&#xff08;地址不变&#xff09; 5.带/…

git学习 1

打开自己想要存放git仓库的文件夹&#xff0c;右键打开git bush&#xff0c;用git init命令建立仓库 用 ls -a(表示全都要看&#xff0c;包括隐藏的)可以看到git仓库 也可以用 git clone 接github链接&#xff08;点code选项里面会给链接&#xff0c;结尾是git的那个&#xf…

OpenHarmony南向开发实例:【智能可燃气体检测系统】

样例简介 本项目是基于BearPi套件开发的智能可燃气体检测Demo&#xff0c;该系统内主要由小熊派单板套件和和MQ5可燃气体检测传感器组成。 智能可燃气体检测系统可以通过云和手机建立连接&#xff0c;可以在手机上控制感应的阈值&#xff0c;传感器感知到的可燃气体浓度超过阈…

C++ | Leetcode C++题解之第12题整数转罗马数字

题目&#xff1a; 题解&#xff1a; const string thousands[] {"", "M", "MM", "MMM"}; const string hundreds[] {"", "C", "CC", "CCC", "CD", "D", "DC&qu…

azkaban的写法

先创建一个.job文件和一个.sql文件 sql语法写到一个test名字的文件里&#xff0c;之后job写法如下&#xff1a; typecommand commandhive -f test6.sql 一定要严格写&#xff0c;不管是字母还是空格&#xff0c;单引号中就是sql文件的名字 然后将它们一块打包&#xff0c;启动…

ubuntu系统逻辑卷Logical Volume扩容根分区

Linux LVM详解 https://blog.csdn.net/qq_35745940/article/details/119054949 https://blog.csdn.net/weixin_41891696/article/details/118805670 https://blog.51cto.com/woyaoxuelinux/1870299 LVM&#xff08;Logical Volume Manager&#xff09;逻辑卷管理&#xff0c…

贪心算法|452.用最少数量的箭引爆气球

力扣题目链接 class Solution { private:static bool cmp(const vector<int>& a, const vector<int>& b) {return a[0] < b[0];} public:int findMinArrowShots(vector<vector<int>>& points) {if (points.size() 0) return 0;sort(p…

rk3588开发板上安装ssh服务

目的&#xff1a;实现远程访问和控制&#xff0c;其他主机远程控制rk3588 方法及操作步骤&#xff1a; 1&#xff09;安装&#xff1a;sudo apt install openssh-server 2&#xff09; 查看运行状态 sudo systemctl status ssh 其它主机远程连接该开发板的ip和端口22即可