Vue实现流程图,借鉴vue-tree-color 实现流程框架技术

这篇具有很好参考价值的文章主要介绍了Vue实现流程图,借鉴vue-tree-color 实现流程框架技术。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Vue实现流程图,借鉴vue-tree-color 实现流程框架技术

借鉴鸣谢

实现组织架构图(vue-org-tree)
如果向使用原来的依赖可以使用这个人的,因为我也是根据这个博客大佬仿照Vue-org-tree实现的方案
对此有几点不惑,问了大佬,大佬也没有回复我

  • className 貌似不起作用,看了文章底部,她也意识到这个问题,但是没有给出详细的解决方案
  • node.js中虽然做了充分的注释,但是她把子节点渲染和主节点渲染给单独分开了,指定了render函数去渲染

演示效果

vue-tree-color,Vue.js,vue.js,流程图,javascript
由于还在启动项目中,还没有完善,目前已经可以支持动态创建节点了
vue-tree-color,Vue.js,vue.js,流程图,javascript
这是通过点击实现抽屉展示
vue-tree-color,Vue.js,vue.js,流程图,javascript
这是通过hover鼠标悬停高亮,由于我并不是一个正儿八经的前端,准确的说我是来搞笑的。所以对于每个节点面板渲染是很简陋的。

引入依赖

# 这里是安装大佬提供的依赖
npm install vue-tree-color

检查less和less-loader,因为该组件使用到了less,可以看看项目工程是否已经安装

npm install --save-dev less less-loader

注意这里可能会在项目启动的时候出现以下问题

报错信息:Syntax Error: TypeError: this.getOptions is not a function

解决方案-需要在安装less和lessloader时,指定版本号,否则就会导致兼容问题。

npm i less@3.9.0 less-loader@4.1.0 -D

添加全局

找到main.js中追加一下内容

import Vue2OrgTree from 'vue-tree-color'
Vue.use(Vue2OrgTree)

组件的二次封装

在公共层面@components 目录下,我们需要进行二次封装,为什么要二次封装,上面已经说到原因了,因为定制化内容不同, 肯定会涉及更改组件渲染方式以及样式,因为二次封装是非常有必要的
vue-tree-color,Vue.js,vue.js,流程图,javascript

步骤1 创建组件目录

我就随便给取了一个名字 SimpleTree 目录

Vue

组件页面,我没做更改,直接拿来用

<template>
  <div class="org-tree-container">
    <div class="org-tree" :class="{horizontal, collapsable}">
      <org-tree-node
        :data="data"
        :props="props"
        :judge="judge"
        :NodeClass="NodeClass"
        :horizontal="horizontal"
        :label-width="labelWidth"
        :collapsable="collapsable"
        :render-content="renderContent"
        :label-class-name="labelClassName"
        @on-expand="(e, data) => $emit('on-expand', e, data)"
        @on-node-focus="(e, data) => $emit('on-node-focus', e, data)"
        @on-node-click="(e, data) => $emit('on-node-click', e, data)"
        @on-node-mouseover="(e, data) => $emit('on-node-mouseover', e, data)"
        @on-node-mouseout="(e, data) => $emit('on-node-mouseout', e, data)"
      />
    </div>
  </div>
</template>
<!--
data:就是数据格式,时一个对象,具体格式下面有示例
horizontal:默认是false,即纵向展示
label-class-name:可以给节点添加的类名,不过我这边使用了没有效果,下面有其他方法来处理节点样式
collapsable:是否折叠,有这个属性,则表示默认折叠,有其他方法可以在存在此属性时,也保证是展开状态
on-expand:点击折叠点,点击可以展开,再次点击可以折叠,是个方法
on-node-click:顾名思义,就是点击节点,触发的事件
on-node-mouseover:鼠标移入节点触发的事件,可以触发一个弹层用于展示详情
on-node-mouseout:鼠标移出节点触发的事件,可以控制详情弹层的隐藏
-->
<script>
import render from './node'

export default {
  name: "SimpleTree",
  components: {
    OrgTreeNode: {
      render,
      functional: true
    }
  },
  props: {
    data: {
      type: Object,
      required: true
    },
    props: {
      type: Object,
      default: () => ({
        id: 'id',
        logo: 'logo',
        key: 'key',
        parentKey: 'parentKey',
        label: 'label',
        expand: 'expand',
        children: 'children',
        type: 'type',
        status: 'status',
        controller: 'controller',
        sys: 'sys',
        remark: 'remark',
        description: 'description',
        registration: 'registration'
      })
    },
    judge: {
      type: Object,
      required: false
    },
    NodeClass: {
      type: Array,
      required: false
    },
    horizontal: Boolean,
    selectedKey: String,
    collapsable: Boolean,
    renderContent: Function,
    labelWidth: [String, Number],
    labelClassName: [Function, String],
    selectedClassName: [Function, String]
  }
}
</script>

<style lang="less">
@import './tree';
</style>

node.js

这个js就是组件渲染的函数,可以稍微研究一下文章来源地址https://www.toymoban.com/news/detail-741245.html

// 判断是否叶子节点
const isLeaf = (data, prop) => {
  return !(Array.isArray(data[prop]) && data[prop].length > 0)
}
// 创建 node 节点
export const renderNode = (h, data, context) => {
  const { props } = context
  const cls = ['org-tree-node']
  const childNodes = []
  const children = data[props.props.children]

  if (isLeaf(data, props.props.children)) {
    cls.push('is-leaf')
  } else if (props.collapsable && !data[props.props.expand]) {
    cls.push('collapsed')
  }

  if (data) {
    childNodes.push(renderLabel(h, data, context))
  }

  if ((!props.collapsable || data[props.props.expand]) && children.length > 0) {
    childNodes.push(renderChildren(h, children, context))
  }

  return h('div', {
    domProps: {
      className: cls.join(' ')
    }
  }, childNodes)
}

// 创建展开折叠按钮
export const renderBtn = (h, data, { props, listeners }) => {
  const expandHandler = listeners['on-expand']

  let cls = ['org-tree-node-btn']

  if (data[props.props.expand]) {
    cls.push('expanded')
  }

  return h('span', {
    domProps: {
      className: cls.join(' ')
    },
    on: {
      click: e => expandHandler && expandHandler(e,data)
    }
  })
}

// 创建 label 节点
export const renderLabel = (h, data, context) => {
  const { props, listeners } = context
  const label = data[props.props.label]
  const renderContent = props.renderContent

  // event handlers
  const clickHandler = listeners['on-node-click']
  const mouseOverHandler = listeners['on-node-mouseover']
  const mouseOutHandler = listeners['on-node-mouseout']

  const childNodes = []
  if (typeof renderContent === 'function') {
    let vnode = renderContent(h, data)

    vnode && childNodes.push(vnode)
  } else {
    childNodes.push(label)
  }

  if (props.collapsable && !isLeaf(data, props.props.children)) {
    childNodes.push(renderBtn(h, data, context))
  }

  const cls = ['org-tree-node-label-inner']
  let { labelWidth, labelClassName, selectedClassName, selectedKey ,judge,NodeClass} = props

  if (typeof labelWidth === 'number') {
    labelWidth += 'px'
  }

  if (typeof labelClassName === 'function') {
    labelClassName = labelClassName(data)
  }

  labelClassName && cls.push(labelClassName)

  // add selected class and key from props
  if (typeof selectedClassName === 'function') {
    selectedClassName = selectedClassName(data)
  }

  selectedClassName && selectedKey && data[selectedKey] && cls.push(selectedClassName)

  return h('div', {
    domProps: {
      className: 'org-tree-node-label'
    }
  }, [h('div', {
    domProps: {
      className:ChangeTheColor(data,judge,NodeClass) + " org-tree-node-label-inner"
    },
    style: { width: labelWidth },
    on: {
      'click': e => clickHandler && clickHandler(e, data),
      'mouseover': e => mouseOverHandler && mouseOverHandler(e, data),
      'mouseout': e => mouseOutHandler && mouseOutHandler(e, data)
    }
  }, childNodes)])
}

function ChangeTheColor(e,judge,NodeClass){
  if(judge !== "" && judge !== undefined && judge !== null && judge.swtich !== false){
    for(var k in judge) {
      var a = (eval("e."+k))
      if(NodeClass){
        for(let c =0 ;c<NodeClass.length;c++){
          if( a === NodeClass[c])
            return  NodeClass[c]
          else if(NodeClass.length-1==c)
            return ""
        }
      }else{
        return ""
      }
    }
  }else{
    return ""
  }
}
// 创建 node 子节点
export const renderChildren = (h, list, context) => {
  if (Array.isArray(list) && list.length > 0) {
    const children = list.map(item => {
      return renderNode(h, item, context)
    })

    return h('div', {
      domProps: {
        className: 'org-tree-node-children'
      }
    }, children)
  }
  return ''
}

export const render = (h, context) => {
  const {props} = context
  if (props.data.id) {
    return renderNode(h, props.data, context)
  } else {
    return ''
  }

}

export default render

tree.less
.org-tree-container {
  display: inline-block;
  padding: 15px;
  background-color: #fff;
}

.org-tree {
  // display: inline-block;
  display: table;
  text-align: center;

  &:before, &:after {
    content: '';
    display: table;
  }

  &:after {
    clear: both;
  }
}

.org-tree-node,
.org-tree-node-children {
  position: relative;
  margin: 0;
  padding: 0;
  list-style-type: none;

  &:before, &:after {
    transition: all .35s;
  }
}
.org-tree-node-label {
  position: relative;
  display: inline-block;

  .org-tree-node-label-inner {
    padding: 10px 15px;
    text-align: center;
    border-radius: 5px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
    -webkit-transition-duration: 0.3s;
    transition-duration: 0.3s;
    cursor: pointer;
  }
  .org-tree-node-label-inner:hover {
    background-color: #c6e2ff;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1)
  }

}

.render-node-panel-header {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  justify-content: center;
  align-items: center;
  margin: 10px 0;
}
.node-box {
  min-width: 200px;
}

.render-node-panel-middle {
  display: flex;
  flex-direction: row;
  justify-content: space-around;
  align-items: center;
}

.org-tree-node-btn {
  position: absolute;
  top: 100%;
  left: 50%;
  width: 20px;
  height: 20px;
  z-index: 10;
  margin-left: -11px;
  margin-top: 9px;
  background-color: #fff;
  border: 1px solid #ccc;
  border-radius: 50%;
  box-shadow: 0 0 2px rgba(0, 0, 0, .15);
  cursor: pointer;
  transition: all .35s ease;

  &:hover {
    background-color: #e7e8e9;
    transform: scale(1.15);
  }

  &:before, &:after {
    content: '';
    position: absolute;
  }

  &:before {
    top: 50%;
    left: 4px;
    right: 4px;
    height: 0;
    border-top: 1px solid #ccc;
  }

  &:after {
    top: 4px;
    left: 50%;
    bottom: 4px;
    width: 0;
    border-left: 1px solid #ccc;
  }

  &.expanded:after {
    border: none;
  }
}
.org-tree-node {
  padding-top: 20px;
  display: table-cell;
  vertical-align: top;

  &.is-leaf, &.collapsed {
    padding-left: 10px;
    padding-right: 10px;
  }

  &:before, &:after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 50%;
    height: 19px;
  }

  &:after {
    left: 50%;
    border-left: 1px solid #ddd;
  }

  &:not(:first-child):before,
  &:not(:last-child):after {
    border-top: 1px solid #ddd;
  }

}
.collapsable .org-tree-node.collapsed {
  padding-bottom: 30px;

  .org-tree-node-label:after {
    content: '';
    position: absolute;
    top: 100%;
    left: 0;
    width: 50%;
    height: 20px;
    border-right: 1px solid #ddd;
  }
}
.org-tree > .org-tree-node {
  padding-top: 0;

  &:after {
    border-left: 0;
  }
}
.org-tree-node-children {
  padding-top: 20px;
  display: table;

  &:before {
    content: '';
    position: absolute;
    top: 0;
    left: 50%;
    width: 0;
    height: 20px;
    border-left: 1px solid #ddd;
  }

  &:after {
    content: '';
    display: table;
    clear: both;
  }
}

.horizontal {
  .org-tree-node {
    // display: flex;
    // flex-direction: row;
    // justify-content: flex-start;
    // align-items: center;
    display: table-cell;
    float: none;
    padding-top: 0;
    padding-left: 20px;

    &.is-leaf, &.collapsed {
      padding-top: 10px;
      padding-bottom: 10px;
    }

    &:before, &:after {
      width: 19px;
      height: 50%;
    }

    &:after {
      top: 50%;
      left: 0;
      border-left: 0;
    }

    &:only-child:before {
      top: 1px;
      border-bottom: 1px solid #ddd;
    }

    &:not(:first-child):before,
    &:not(:last-child):after {
      border-top: 0;
      border-left: 1px solid #ddd;
    }

    &:not(:only-child):after {
      border-top: 1px solid #ddd;
    }

    .org-tree-node-inner {
      display: table;
    }

  }

  .org-tree-node-label {
    display: table-cell;
    vertical-align: middle;
  }

  &.collapsable .org-tree-node.collapsed {
    padding-right: 30px;

    .org-tree-node-label:after {
      top: 0;
      left: 100%;
      width: 20px;
      height: 50%;
      border-right: 0;
      border-bottom: 1px solid #ddd;
    }
  }

  .org-tree-node-btn {
    top: 50%;
    left: 100%;
    margin-top: -11px;
    margin-left: 9px;
  }

  & > .org-tree-node:only-child:before {
    border-bottom: 0;
  }

  .org-tree-node-children {
    // display: flex;
    // flex-direction: column;
    // justify-content: center;
    // align-items: flex-start;
    display: table-cell;
    padding-top: 0;
    padding-left: 20px;

    &:before {
      top: 50%;
      left: 0;
      width: 20px;
      height: 0;
      border-left: 0;
      border-top: 1px solid #ddd;
    }

    &:after {
      display: none;
    }

    & > .org-tree-node {
      display: block;
    }
  }
}

使用

组件引入使用

 <simple-tree
        :data="orgData"
        :horizontal="horizontalRadio"
        :label-class-name="labelClassName"
        :collapsable="collapsableRadio"
        @on-expand="onExpand"
        @on-node-click="NodeClick"
        @on-node-mouseover="onMouseover"
        @on-node-mouseout="onMouseout"
        :renderContent="renderContent"/>
  • 参数说明:
data: 就是树形结构,下面会有案例
horizontal:排列形式
collapsable: 是否展开
  • 函数说明:
on-expand: 展开、闭合节点
on-node-click: 节点点击事件
on-node-mouseover:鼠标悬停
on-node-mouseout:鼠标悬出
renderContent:渲染函数

数据结构案例

data(){
	return{
	  labelClassName: "bg-color-orange", // 看到node.js 中这个实际没用的
      basicInfo: { id: null, label: null },
      basicSwitch: false,
      data: {
        id: 0,
        label: "XXX科技有限公司",
        className: 'nxnnxnxn', // 切记HTML的class 标签属性不能以数字开头
        children: [
          {
            id: 2,
            label: "产品研发部",
            className: 'nxnnxnxn-1',
            children: [
              {
                id: 5,
                label: "研发-前端",
                children: [
                  {
                    id: 55,
                    className: 'nxnnxnxn-55',
                    label: "前端1"
                  },
                  {
                    id: 56,
                     className: 'nxnnxnxn-56',
                    label: "前端2"
                  },
                  {
                    id: 57,
                    className: 'nxnnxnxn-57',
                    label: "前端3"
                  },
                  {
                    id: 58,
                     className: 'nxnnxnxn-58',
                    label: "前端4"
                  }
                ]
              }
            ]
          }
        ]
      },
	}
},
methods:{
	//渲染节点
	renderContent(h, data) {
	// 通过data中的className属性来对div元素进行注入class
	// 每个节点渲染必然会走这个函数
	//这里对应的不同的className 需要在上面的tree.less中写入样式
      return (
        <div class = {data.className}>
          <div>
            <i class="el-icon-user-solid"></i>
            <span>{data.label}</span>
            <span></span>
          </div>
          <div style="font-size:12px;line-height:20px;">测试人员</div>
        </div>
      );
    },
    //鼠标移出
    onMouseout(e, data) {
       console.log("onMouseout", data)
    },
    //鼠标移入
    onMouseover(e, data) {
     console.log("onMouseover", data)
    },
    //点击节点
    NodeClick(e, data) {
      console.log(e, data);
    },
    //默认展开
    toggleExpand(data, val) {
      if (Array.isArray(data)) {
        data.forEach(item => {
          this.$set(item, "expand", val);
          if (item.children) {
            this.toggleExpand(item.children, val);
          }
        });
      } else {
        this.$set(data, "expand", val);
        if (data.children) {
          this.toggleExpand(data.children, val);
        }
      }
    },
    collapse(list) {
      list.forEach(child => {
        if (child.expand) {
          child.expand = false;
        }
        child.children && this.collapse(child.children);
      });
    },
    //展开
    onExpand(e, data) {
      if ("expand" in data) {
        data.expand = !data.expand;
        if (!data.expand && data.children) {
          this.collapse(data.children);
        }
      } else {
        this.$set(data, "expand", true);
      }
    },
}

到了这里,关于Vue实现流程图,借鉴vue-tree-color 实现流程框架技术的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 如何自己实现一个丝滑的流程图绘制工具(一)vue如何使用

    背景 项目需求突然叫我实现一个类似processOn一样的在线流程图绘制工具。 这可难倒我了,立马去做调研,在github上找了很多个开源的流程图绘制工具, 对比下来我还是选择了 bpmn-js 原因: 1、他的流程图是涉及到业务的,比如开始事件、结束事件等 2、扩展性很强(这个扩展

    2024年02月11日
    浏览(51)
  • vue使用jsplumb 流程图

    安装jsPlumb库:在Vue项目中使用npm或yarn安装jsPlumb库。 npm install jsplumb 创建一个Vue组件:创建一个Vue组件来容纳jsPlumb的功能和呈现。 效果图:  初始化jsPlumb一定要在mounted函数里面,要执行在dom渲染完成的时候 一定要设置绑定的容器,不然连线的容器外加入任何其他布局元素

    2024年02月12日
    浏览(38)
  • Vue使用AntV X6绘制流程图(组态呈现)

    先上几个网址 AntV | 蚂蚁数据可视化 AntV X6 图编辑引擎 AntV X6文档 AntV X6 API AntV X6图表示例 上面是一些用到的网址,先说下我项目中的需求,就是要自己绘制出一个我们想要的图,组态化呈现出来,然后这个图中会有很多节点,每个节点都会有自己的状态 ,状态会实时改变,

    2024年02月06日
    浏览(66)
  • 在vue3中使用pinia完整流程图文

    使用vite创建好一个vue3项目,开发语言选择ts 使用 npm i pinia -s 安装最新版本的pinia 这里我的版本安装的是 2.1.4 1.在main中注册pinia 2.在store中创建index.ts和store-name.ts文件 index.ts内容如下: store-name.ts内容如下: app.vue文件的内容如下: 页面输出如下内容则一次简单的pinia的调用完

    2024年02月10日
    浏览(42)
  • vue2中实现Antv g6 流程图 以及自定义节点

    本案例 antv g6版本: \\\"@antv/g6\\\": \\\"^3.4.8\\\", 效果: 1.引入antv g6和echarts差不多 一定记得给盒子设置宽高 初学者一个,以上如有问题或者错误,多谢指出

    2024年02月13日
    浏览(56)
  • 使用antv/G6在vue项目中开发较复杂样式流程图

    设计师提供了一版样式较复杂的流程图,我搜了一些常用的vue-super-flow和vue-x6-flow等都只支持简单的样式。之前自己写过纯展示流程图不涉及太多交互,感觉还是找一个成熟的插件开发更适合,也方便其他同事参考,所以最后选择了用antv/G6自己个性化开发,总结了使用antv/G6在

    2023年04月09日
    浏览(120)
  • vue3+ts 绘制流程图 vueflow 附代码及效果图

    已完成渲染流程图,自定义模板内容(上下分级),自定义样式,新增节点addRandomNode,点击修改节点nodeClickHandler(从父组件传值) 官网:https://vueflow.dev/guide/node.html#node-template 文档比较复杂,很多想找的方法没法一下就找到需要注意 我装了三个,如果npm安装报错可以试试yarn add

    2024年02月12日
    浏览(36)
  • 【vue2+antvx6】节点大小不一致,点击按钮流程图自动布局

    需求: 1、点击优化布局的按钮,自动布局(从左到右),按钮变成撤销布局按钮 2、点击撤销布局的按钮,返回之前的布局 3、在点击优化布局的按钮后,如果移动了节点,则自动将撤销布局的按钮变成优化布局的按钮 第一步:安装插件 第二步:写方法  全部代码

    2024年04月28日
    浏览(30)
  • 使用Jsmind实现前端流程图功能

    需求:实现流程图功能,根据状态不同显示不同的颜色,点击有对应的点击颜色 思想:根据jsmind构建思维导图,改变节点背景颜色,获取点击节点事件当点击节点是设置节点选中背景图片。 注意: 由于jsmind更新各版本api都有很大改动,所以我使用的都是官方文档注明的基于各

    2024年02月03日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包