用flex布局实现一个流程设计器

news2024/11/23 2:23:37

最近接到一个需求,要做一个流程设计的功能,大概长下面这个样子:

支持添加、编辑和删除节点,节点只有四种类型:开始节点、普通节点、分支节点、结束节点。

因为每个节点只有一个进和一个出,且节点不需要支持拖拽,连线也是自动连接,所以整体比较简单,不用开源库,自己做的成本也不会很高。

初看其实比较麻烦的只有布局和连线,布局因为节点不需要支持拖拽,所以位置都是自动且固定的,更精确点说其实就是垂直居中,说到居中,你可能会想到flex布局,那么这里能不能使用flex布局呢,显然是可以的,另外连线通常可能会使用svg,但是其实直接使用div和伪元素也完全可以实现。

接下来我们就从零来实现一下,因为我们的项目原因,所以还是会基于Vue2版本来实现。

数据结构

整体数据是一个数组,数组的每一项代表一个节点。

开始节点

{ id: 'startEvent', type: 'start', title: '开始' }

id除了开始和结束节点外,其他节点的id随机生成即可,type代表节点的类型,title为节点的标题。

结束节点

{ id: 'endEvent', type: 'end', title: '结束' }

普通节点

{
    id: '随机id',
    type: 'normal',
    title: '审批人',
    content: '主管',// 节点内容
    configData: {},// 节点配置数据
    nodeList: []// 后续节点
}

默认titlecontent的内容会在节点上显示,而针对每个节点的配置数据保存在configData上,一般情况下,顶层节点会直接作为数组的一项,而当处于条件分支中时,则需要把后续节点保存在nodeList上。

分支节点

{
    id: '随机id',
    type: 'condition',
    title: '条件分支',
    children: [// 分支
        // 普通节点
    ]
}

分支节点的分支保存在children属性上,每个分支节点其实就是一个普通节点,普通节点里又可以嵌套分支节点。

布局

入口组件

首先创建一个入口组件:

<template>
  <div class="sfcContainer">
    <div class="sfcContent">
      <Node v-for="node in data" :key="node.id" :data="node"></Node>
    </div>
  </div>
</template>

<script>
export default {
  name: 'SimpleFlowChart',
  props: {
    data: {
      type: Array,
      default() {
        return []
      }
    }
  }
}
</script>

<style lang="less" scoped>
.sfcContainer {
  width: 100%;
  height: 100%;
  overflow: auto;// 超出显示滚动条
  box-sizing: border-box;
  background: rgba(0, 0, 0, 0.03);

  * {
    box-sizing: border-box;
  }

  .sfcContent {
    // 设置垂直居中
    display: flex;
    align-items: center;
    padding: 20px;
    // 最小宽高设为容器宽高,否则无法居中
    min-width: 100%;
    min-height: 100%;
    // 否则宽高以实际的内容为准
    width: max-content;
    height: max-content;
  }
}
</style>

流程数据通过props传入,循环数据渲染 Node组件,Node组件为所有节点组件的容器。

css中给sfcContent元素设置的display: flex;align-items: center;很关键,就是这两行样式,使得所有顶层节点可以水平排列并垂直居中。

基础组件Node

这个组件作为所有节点组件的容器,只要根据类型渲染不同节点组件即可:

<template>
  <div class="sfcNodeContainer">
    <!-- 开始节点 -->
    <StartNode v-if="data.type === 'start'" :data="data"></StartNode>
    <!-- 结束节点 -->
    <EndNode v-else-if="data.type === 'end'" :data="data"></EndNode>
    <!-- 分支节点 -->
    <ConditionNode v-else-if="data.type === 'condition'" :data="data"></ConditionNode>
    <!-- 普通节点 -->
    <NormalNode v-else :data="data"></NormalNode>
  </div>
</template>

<script>
export default {
  name: 'Node',
  props: {
    data: {
      type: Object,
      default: null
    }
  }
}
</script>

开始节点StartNode、结束节点组件EndNode

开始节点和结束节点差不多,除了样式稍微有点差别外,就是开始节点有根指向下一个节点的箭头线。

开始节点组件:

<template>
  <div class="sfcStartNodeContainer">
    <div class="sfcStartNodeContent">{{ data.title }}</div>
  </div>
</template>

<script>
export default {
  name: 'StartNode',
  props: {
    data: {
      type: Object,
      default: null
    }
  }
}
</script>

箭头线下一个小节再看,节点的基础样式因为不影响布局所以也没贴出来。

结束节点组件:

<template>
  <div class="sfcEndNodeContainer">{{ data.title }}</div>
</template>

<script>
export default {
  name: 'EndNode',
  props: {
    data: {
      type: Object,
      default: null
    }
  }
}
</script>

<style lang="less" scoped>
.sfcEndNodeContainer {
  // 省略不影响布局的节点样式
}
</style>

普通节点组件NormalNode

<template>
  <div class="sfcNormalNodeContainer">
    <!--节点内容-->
    <div class="sfcNormalNodeWrap">
      <div class="sfcNormalNodeContent">
        <div class="sfcNormalNodeTitle">
          {{ data.title || '' }}
        </div>
        <!--省略-->
      </div>
    </div>
    <!--递归渲染后续Node组件-->
    <Node v-for="node in (data.nodeList || [])" :key="node.id" :data="node"></Node>
  </div>
</template>

<script>
export default {
  name: 'NormalNode',
  props: {
    data: {
      type: Object,
      default: null
    }
  }
}
</script>

<style lang="less" scoped>
.sfcNormalNodeContainer {
  position: relative;
  // 使当前节点的内容和后续节点水平排列,并且垂直居中
  display: flex;
  align-items: center;
  flex-shrink: 0;

  // 省略节点基础样式
}
</style>

sfcNormalNodeWrap元素渲染节点自身的内容,如果当前节点的nodeList中有后续节点,那么遍历递归渲染Node节点。

通过在容器上设置display: flex样式,让节点自身内容和后续其他节点水平排列显示,再通过align-items: center样式让它们垂直居中对齐。

分支节点组件ConditionNode

<template>
  <div class="sfcConditionNodeContainer">
    <div class="sfcConditionNodeItemList">
      <div
        class="sfcConditionNodeItem"
        v-for="node in data.children"
        :key="node.id"
      >
        <div class="sfcConditionNodeItemNodeWrap">
          <Node :data="node"></Node>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'ConditionNode',
  props: {
    data: {
      type: Object,
      default: null
    }
  }
}
</script>

分支节点自身其实没有实际内容,只是作为一个容器来渲染childen中的具体分支节点,分支节点其实就是普通节点,所以遍历渲染Node组件传入数据即可。

到目前为止所有节点组件就已经创建完毕了,传入数据看一下现在的效果:

可以看到大体上已经成型了,只要连上线就大功告成了。

连线

箭头组件

箭头线的样式其实是一样的,所以我们创建一个箭头线的组件ArrowLine

<template>
  <div class="sfcArrowLine"></div>
</template>

<script>
export default {
  name: 'ArrowLine'
}
</script>

<style lang="less" scoped>
.sfcArrowLine {
  position: relative;
  width: 65px;
  user-select: none;

  &:before {
    position: absolute
    top: 0;
    left: 0;
    transform: translateY(-50%);
    height: 2px;
    width: 100%;
    background-color: #dedede;
    content: '';
  }

  &:after {
    position: absolute;
    width: 0;
    height: 0;
    border-left: 10px solid #dedede;
    border-top: 6px solid transparent;
    border-bottom: 6px solid transparent;
    content: '';
    right: 0;
    top: 0;
    transform: translateY(-50%);
  }
}
</style>

线使用before元素绘制,箭头三角形使用after元素绘制。

开始节点添加箭头

首先在开始节点中引入箭头组件:

<template>
  <div class="sfcStartNodeContainer">
    <div class="sfcStartNodeContent">{{ data.title }}</div>
    <ArrowLine></ArrowLine>
  </div>
</template>

效果如下:

箭头应该在右边,很简单,flex大法:

<style lang="less" scoped>
.sfcStartNodeContainer {
  display: flex;
  align-items: center;
}
</style>

普通节点添加箭头

同样先引入箭头组件:

<template>
  <div class="sfcNormalNodeContainer">
    <!--节点内容-->
    <div class="sfcNormalNodeWrap">
      <div class="sfcNormalNodeContent">
        <div class="sfcNormalNodeTitle">
          {{ data.title || '' }}
        </div>
        <!--省略-->
      </div>
      <!--箭头组件放在这里-->
      <ArrowLine></ArrowLine>
    </div>
    <!--递归渲染后续Node组件-->
    <Node v-for="node in (data.nodeList || [])" :key="node.id" :data="node"></Node>
  </div>
</template>

同样需要设置成flex布局:

<style lang="less" scoped>
    .sfcNormalNodeWrap {
        display: flex;
        align-items: center;
    }
</style>

分支节点添加箭头

<template>
  <div class="sfcConditionNodeContainer">
    <div class="sfcConditionNodeItemList">
      <div
        class="sfcConditionNodeItem"
        v-for="node in data.children"
        :key="node.id"
      >
        <div class="sfcConditionNodeItemNodeWrap">
          <Node :data="node"></Node>
        </div>
      </div>
    </div>
    <ArrowLine></ArrowLine>
  </div>
</template>

和分支节点列表并列,同样少不了flex样式:

<style lang="less" scoped>
.sfcConditionNodeContainer {
  display: flex;
  align-items: center;
}
</style>

到这里整体的效果如下:

离胜利只有一步之遥了。

完善分支节点的连线

首先给分支节点加个间距,现在都挨着一起:

<template>
  <div class="sfcConditionNodeContainer">
    <div class="sfcConditionNodeItemList">
      <div
        class="sfcConditionNodeItem"
        v-for="node in data.children"
        :key="node.id"
      >
        <div class="sfcConditionNodeItemNodeWrap">
          <Node :data="node"></Node>
        </div>
      </div>
    </div>
  </div>
</template>

<style lang="less" scoped>
.sfcConditionNodeItem {
  padding: 30px;
}
</style>

连接分支整体的竖线

需要添加如下的竖线:

仔细观察可以发现其实就是给分支节点的前后各添加一竖线,其中的间距其实是因为前面我们给分支节点的每个节点都设置了一个30pxpadding,但是其实尾部的间距是不需要的:

所以我们修改一下,把右内边距设为0

<style lang="less" scoped>
.sfcConditionNodeItem {
  padding: 30px;
  padding-right: 0;
}
</style>

你可能会想直接在分支节点的容器元素sfcConditionNodeContainer上直接前后绘制两条线,但是问题是这根线不是100%和容器元素一样高的,而是延伸到最外侧两个分支的高度的一半,通过纯css其实很难绘制出来,所以我们可以换种方法,让每个分支自己来绘制,这样其实就把一根线分成几段:

具体来说,就是最外侧的两个分支画一根一半高度的线,中间的分支画一根和高度一样的线。

要添加的线比较多,伪元素不够用,所以我们通过div元素来作为连线,然后通过绝对定位来显示。

<template>
  <div class="sfcConditionNodeContainer">
    <div class="sfcConditionNodeItemList">
      <div
        class="sfcConditionNodeItem"
        v-for="node in data.children"
        :key="node.id"
      >
        <!-- 左侧的竖线 -->
        <div
          class="sfcConditionNodeItemLine sfcConditionNodeItemFirstLine"
        ></div>
        <!-- 右侧的竖线 -->
        <div
          class="sfcConditionNodeItemLine sfcConditionNodeItemLastLine"
        ></div>
        <div class="sfcConditionNodeItemNodeWrap">
          <Node :data="node"></Node>
        </div>
      </div>
    </div>
  </div>
</template>

<style lang="less" scoped>
    .sfcConditionNodeItem {
        position: relative;// 设置相对定位

        // 前后竖线
        .sfcConditionNodeItemLine {
            position: absolute;
            height: 100%;// 默认为中间分支的的竖线,高度100%
            width: 2px;
            left: 0px;
            top: 0;
            background-color: #dedede;

            // 右侧竖线距离左侧为100%
            &.sfcConditionNodeItemLastLine {
                left: 100%;
            }
        }

        // 最外侧的两个分支的竖线高度为50%
        &:first-of-type {
            // 最顶部的分支的竖线距顶部50%
            > .sfcConditionNodeItemLine {
                top: 50%;
                height: 50%;
            }
        }
        &:last-of-type {
            // 最底部的分支的竖线距顶部0
            > .sfcConditionNodeItemLine {
                top: 0;
                height: 50%;
            }
        }
    }
</style>

效果如下:

连接分支整体和分支的水平线

画完了竖线,接下来是水平线,如下所示,我们要连接分支左侧竖线和分支节点:

这根线的宽度其实就是padding的大小,然后left0top50%,同样使用div来绘制:

<template>
  <div class="sfcConditionNodeContainer">
    <div class="sfcConditionNodeItemList">
      <div
        class="sfcConditionNodeItem"
        v-for="node in data.children"
        :key="node.id"
      >
        <div
          class="sfcConditionNodeItemLine sfcConditionNodeItemFirstLine"
        ></div>
        <div
          class="sfcConditionNodeItemLine sfcConditionNodeItemLastLine"
        ></div>
        <!-- 连接竖线和节点的水平线 -->
        <div class="sfcConditionNodeItemLinkLine"></div>
        <div class="sfcConditionNodeItemNodeWrap">
          <Node :data="node"></Node>
        </div>
      </div>
    </div>
  </div>
</template>

<style lang="less" scoped>
    // 连接竖线和节点的水平线
    .sfcConditionNodeItemLinkLine {
        position: absolute;
        width: 30px;
        height: 2px;
        left: 0px;
        top: 50%;
        transform: translateY(-50%);// 让线段真正居中
        background-color: #dedede;
    }
</style>

连接较短分支和分支整体右侧的水平线

最后还剩下如下图所示的较短分支和分支整体右侧的水平线:

这个也很简单,在每个分支的节点后面添加一个div作为连线,和分支节点作为兄弟节点,父级设置flex布局,连线宽度自适应即可:

<template>
  <div class="sfcConditionNodeContainer">
    <div class="sfcConditionNodeItemList">
      <div
        class="sfcConditionNodeItem"
        v-for="node in data.children"
        :key="node.id"
      >
        <div
          class="sfcConditionNodeItemLine sfcConditionNodeItemFirstLine"
        ></div>
        <div
          class="sfcConditionNodeItemLine sfcConditionNodeItemLastLine"
        ></div>
        <div class="sfcConditionNodeItemLinkLine"></div>
        <div class="sfcConditionNodeItemNodeWrap">
          <Node :data="node"></Node>
          <!-- 连接较短分支和分支整体右侧的水平线 -->
          <div class="sfcConditionNodeItemLinkCrossLine"></div>
        </div>
      </div>
    </div>
  </div>
</template>

<style lang="less" scoped>
    .sfcConditionNodeItemNodeWrap {
        // 父级设置flex布局,让连线和节点整体垂直居中
        display: flex;
        align-items: center;

        // 连接较短分支和分支整体右侧的水平线
        .sfcConditionNodeItemLinkCrossLine {
            height: 2px;
            flex-grow: 1;// 连线宽度自适应,填充剩余空间
            background-color: #dedede;
        }
    }
</style>

到这里,节点布局以及连线都已完成,最终效果如下:

是不是很简单。

新增、编辑、删除节点

新增节点

新增节点首先需要在每一个节点后面的连接线上添加一个按钮,点击按钮后选择要添加的节点的类型,然后进行添加。

除了分支节点外,只能添加普通节点,但是对于流程设计的业务来说,可以细分为很多类型,比如审批人、抄送人、发送短信等等,这个不同的业务可能不一样,所以肯定不能写死,需要开放出来可自定义。

首先创建一个添加节点的按钮组件:

<template>
  <div class="sfcAddNode">
    <div class="sfcAddNodeBtn">
      <!-- 省略 -->
    </div>
  </div>
</template>

<script>
export default {
  name: 'AddNode'
}
</script>

<style lang="less" scoped>
.sfcAddNode {
  position: absolute;
  right: 0;
  top: 0;
  width: 65px;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;

  .sfcAddNodeBtn {
    // 忽略按钮样式
  }
}
</style>

按钮组件绝对定位,宽度和箭头线宽度一致,为65px,高度100%,和节点一致,相当于覆盖在箭头线上,然后通过flex布局让真正的按钮居中即可。

先给普通节点加上:

<template>
  <div class="sfcNormalNodeContainer">
    <div class="sfcNormalNodeWrap">
      <div class="sfcNormalNodeContent">
          <!-- 省略 -->
      </div>
      <ArrowLine></ArrowLine>
      <!-- 添加节点组件 -->
      <AddNode></AddNode>
    </div>
    <Node v-for="node in (data.nodeList || [])" :key="node.id" :data="node"></Node>
  </div>
</template>

<style lang="less" scoped>
    .sfcNormalNodeWrap {
        position: relative;// ++
    }
</style>

接下来给每个分支也加上:

<template>
  <div class="sfcConditionNodeContainer">
    <div class="sfcConditionNodeItemList">
      <!-- 省略 -->
    </div>
    <ArrowLine></ArrowLine>
    <AddNode></AddNode>
  </div>
</template>

按钮默认都显示可能不太好看,可以隐藏起来,鼠标滑入按钮组件区域再显示。

然后当鼠标移入按钮时显示可添加的节点类型,点击要添加的节点类型后进行添加。添加一个节点其实就是往数组里插入一项,但不同的节点对应的数组是不一样的,如下图所示:

顶层节点添加下一个节点需要把节点插入顶层数组,分支里的节点插入下一个节点需要插入到自己的nodeList数组里,所以实现时需要区分一下。

当然分支也是可以添加条件的:

点击后往分支节点的children数组里添加一项即可。

效果如下:

点击添加节点和条件_20221229094533.gif

编辑

编辑主要是当点击某个节点以后可以修改节点标题,节点配置,节点显示的内容一般也是来自节点的配置。

所以对于库来说只要抛出一个点击事件即可,具体的编辑界面用户可根据业务自行开发。

点击节点显示侧边栏_20221229094520.gif

删除

当鼠标悬浮到节点内容上显示一个删除按钮,点击后删除掉当前节点即可,对于条件分支来说,如果删除到仅剩一个分支,那么这个条件分支也就没有了意义,直接整个条件分支自动删除。

删除节点_20221229094505.gif

自定义节点内容

因为组件树层级比较深,所以通过slot自定义节点内容不是很方便,所以我选择了一个比较low的方式,即将节点内容单独抽成一个组件,然后在注册组件的时候提供选项配置,那么如果想自定义节点内容,很简单,不要使用内置的节点内容组件,自行编写并注册一个即可,使用约定的组件名称就可以了。

const install = function (Vue, { notRegisterNodeContent } = {}) {
  Vue.component(ConditionNode.name, ConditionNode);
  Vue.component(EndNode.name, EndNode);
  Vue.component(Node.name, Node);
  Vue.component(NormalNode.name, NormalNode);
  Vue.component(StartNode.name, StartNode);
  Vue.component(Index.name, Index);
  // 需要自定义节点内容时通过选项参数指定不要注册内置节点内容组件即可
  if (!notRegisterNodeContent) {
    Vue.component(NodeContent.name, NodeContent);
  }
};

export default {
  install
};

然后自己编写一个内容节点并注册:

Vue.component(CustomNodeContent.name, CustomNodeContent)
Vue.use(SimpleFlowChart, {
  notRegisterNodeContent: true
})

同样添加节点悬浮面板也可以通过这种方式自定义。

垂直排列

支持垂直排列也很简单,基本上只要在所有设置了display:flex的地方加上flex-direction: column;,然后再把连线由竖的改成水平的,位置调一下就可以了:

最后

本文详细的介绍了一下如何使用flex布局实现一个简单的流程设计器,demo及完整的源码如下:

demo:https://wanglin2.github.io/simple-flow-chart。

源码:https://github.com/wanglin2/simple-flow-chart。

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

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

相关文章

一文扫盲 OA、CRM、ERP、MES、HRM、SCM、WMS、KMS 等B端系统

OA系统 &#xff08;Office Automation System&#xff0c;办公自动化系统&#xff09;&#xff1a;OA系统是一种用于协调、管理和优化办公流程的软件系统&#xff0c;包括电子邮件、日程安排、文档管理、工作流程管理等功能模块&#xff0c;帮助企业提高工作效率和管理水平。…

C#程序的内存映射文件解析

一、背景 前段时间训练营里有朋友问 内存映射文件 是怎么玩的&#xff1f;说实话这东西理论我相信很多朋友都知道&#xff0c;就是将文件映射到进程的虚拟地址&#xff0c;说起来很容易&#xff0c;那如何让大家眼见为实呢&#xff1f;可能会难倒很多人&#xff0c;所以这篇我…

《项目实战》构建SpringCloud alibaba项目

文章目录 1、概要2、整体架构流程2.1、技术结构组成部分 3、技术名词解释4、技术细节4.1、构建父工程4.1.1、选择构建Maven项目4.1.2、修改父工程文件4.1.3、修改父工程pom.xml配置4.1.3.1、添加springboot支持4.1.3.2、修改JDK版本、编码、springboot版本配置4.1.3.3、添加Spr…

自定义MaterialEditText

自定义MaterialEditText 日记 现在都不流行写博客了&#xff0c;因为这玩意都认为对于面试没啥用&#xff0c;我感觉很多事情不应该太功利。所谓博客还是更多的应该用来进行自己日常学习的归纳和总结&#xff0c;而不是去贪图所谓的面试加分。因为面试可能是一时的&#xff0…

Apple Vision Pro的价格并没有看起来那么疯狂

When Apple announced the price of their groundbreaking new mixed reality headset, the Vision Pro, jaws around the world collectively dropped. At a hefty $3,499, it’s not for everyone, but is it really so unreasonable if we take a closer look? 当苹果宣布其…

CSS特性、背景属性和显示模式

CSS特性 CSS特性&#xff1a;化简代码 / 定位问题&#xff0c;并解决问题 继承性层叠性优先级 继承性 继承性&#xff1a;子级默认继承父级的文字控制属性。 注意&#xff1a;如果标签有默认文字样式会继承失败。 例如&#xff1a;a 标签的颜色、标题的字体大小。 层叠性 …

前端 sentry 接入钉钉机器人

sentry 接入钉钉机器人 打开钉钉,添加机器人 此时会得到Webhook地址,记录一下,以后会用到 sentry 端设置 看看这里有木有钉钉插件,有的话开启插件,并配置这里我说一下没有的情况下,我们何如设置 这里需要填写webhook url 这个的url 需要是一个公网的地址,不可以是本地…

HID协议学习

HID协议学习 0. 文档资料 USB_HID协议中文版_USB接口HID设备_AUJsRmB9kg.pdf HID报告描述符精细说明_mgCxM8_ci9.pdf hut1_22_U3cvnwn_ZZ.pdf 1. 基本概念 HID协议是一种基于USB的通讯协议&#xff0c;用于在计算机和输入设备之间进行数据传输。HID协议定义了标准的数据格…

动态规划算法(子数组专题1)

动态规划算法专辑之子数组问题&#xff08;1&#xff09; 本专栏将从状态定义、状态转移方程、初始化、填表顺序、返回值这五大细节来详细讲述动态规划的算法的解题思路及代码实现一、什么是子数组 子数组&#xff1a;子数组是数组中的一个连续部分的集合&#xff0c;子序列可…

Python+Selenium UI自动化测试环境搭建及使用

目录 一、什么是Selenium &#xff1f; 二、Selenium环境搭建 三、WebDriver API 总结&#xff1a; 一、什么是Selenium &#xff1f; Selenium 是一个浏览器自动化测试框架&#xff0c;它主要用于web应用程序的自动化测试&#xff0c;其主要特点如下&#xff1a;开源、免费…

缅怀(上次写博客是2009年10月24日)

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

Nucleo-F411RE (STM32F411)LL库体验 3 - 滴嗒定时器的配置

Nucleo-F411RE &#xff08;STM32F411&#xff09;LL库体验 3 - 滴嗒定时器的配置 1、LL库延时 LL库初始化时钟的时候调用了LL_Init1msTick(100000000)函数&#xff0c;这个函数其实就是初始化了系统的滴答定时器。 LL_InitTick原型如下&#xff1a; load值 sysclk/1000&a…

RocketMQ架构和工作流程

一.MQ概述 1.简介 MQ&#xff0c;Message Queue&#xff0c;是一种提供消息队列服务的中间件&#xff0c;也称为消息中间件&#xff0c;是一套提供了消息生产、存储、消费全过程API的软件系统。消息即数据。一般消息的体量不会很大。 2.用途 限流削峰 MQ可以将系统的超量请求…

接口测试工具怎么选?这个技巧你一定要知道

目录 前言 一、易用性 二、灵活性 三、可靠性 测试用例 接口测试数据 自动化测试 测试报告 总结 前言 当今软性开发中&#xff0c;接口测试已成为必不可少的一环&#xff0c;该如何选择接口测试工具?选择合适的接口测试工具对于程房员来说非常重要&#xff0c;因为…

SQL死锁

前言&#xff1a; 使用脚本刷数据时&#xff0c;开多线程经常遇到死锁现象&#xff0c;面试也经常问到&#xff0c;故开此篇 日志错误示例&#xff1a; ### Error updating database. Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Deadlock fo…

Tplink企业版开启ipv6

Tplink企业版开启ipv6 1、登录路由器 路由器的默认地址一般为&#xff1a;192.168.0.1&#xff0c;登录成功后如下图&#xff1a; 2、WAN设置ipv6 wan是设置启用ipv6模式&#xff0c;如果这里无法启用&#xff0c;主要是因为“接口模式”中启用了桥接模式&#xff0c;可以关闭…

多线程详解

多线程详解 Process和Thread 程序是指令和数据的有序结合&#xff0c;其本身没有任何运行的含义&#xff0c;是一个静态的概念 进程是执行程序的一次执行过程&#xff0c;是一个动态的概念&#xff0c;是系统资源分配的单位 通常在一个进程中可以包含若干个线程。线程是CPU调…

(数组) 922. 按奇偶排序数组 II ——【Leetcode每日一题】

❓922. 按奇偶排序数组 II 难度&#xff1a;简单 给定一个非负整数数组 nums&#xff0c; nums 中一半整数是 奇数 &#xff0c;一半整数是 偶数 。 对数组进行排序&#xff0c;以便当 nums[i] 为奇数时&#xff0c;i 也是 奇数 &#xff1b;当 nums[i] 为偶数时&#xff0c…

开发语言的更新换代,都是为了更好地提高生产力,Kotlin也是如此~

作为一名Android开发&#xff0c;学习Kotlin是很有必要的。以下是一些原因&#xff1a; 1.Kotlin是官方支持的语言。 在2017年Google宣布支持Kotlin作为官方开发语言后&#xff0c;Kotlin已成为Android生态系统的重要组成部分。此举表明Kotlin的发展前景非常广阔&#xff0c;…

uniapp兼容多pda扫描扫码

前景 网上有现成的针对单个pda扫码录入的代码&#xff0c;但是公司的需求是在多个不同厂商pda上运行&#xff0c;这就会导致不同的pda默认的广播动作和广播标签不一致的情况&#xff0c;目前也没有获取这俩字段的api。 单个pda扫描扫码代码 先创建一个scanCode.js的文件 le…