vue2中流程图、组织架构图实现

在开发过程中,我们往往会使用到权限映射这样的架构图,也就是我们常说的组织架构,废话不多说,直接上代码…

演示效果

在这里插入图片描述

Vue实现流程图,借鉴vue-tree-color

引入依赖
npm install vue-tree-color

同时查看项目中是否已安装less和less-loader,因为该组件使用到less

npm install --save-dev less less-loader

如果这里启动项目报错,有可能是less和less-loader的版本过高,可以降低版本,或者指定版本号

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

添加全局
import Vue2OrgTree from 'vue-tree-color'
Vue.use(Vue2OrgTree)

组件二次封装

创建components文件夹,在components下加入下面两个文件
在这里插入图片描述

  1. vueOrgTree.vue文件
<template>
  <div ref='container' class='container'>
    <div v-if='isShow' class='spin'>
      <a-spin tip='数据加载中...' />
    </div>
    <vueOrgTree
      v-if='!isShow'
      class='vueOrgTree'
      :data='data'
      :horizontal='true'
      :collapsable='true'
      @on-expand='onExpand'
      @on-node-click='NodeClick'
      @on-node-mouseover='onMouseover'
      @on-node-mouseout='onMouseout'
      :renderContent='renderContent'


    />
  </div>
</template>
<script>
import vueOrgTree from './Components/vueOrgTree.vue'
import throttle from 'lodash/throttle'
//导入数据
import { data, img, className } from './index'

export default {
  name: 'permissionMap',
  components: {
    vueOrgTree
  },
  data() {
    return {
      win: '',
      container: '',
      handler: () => {
      },
      isShow: true,
      data: {}

    }
  },
  created() {

  },
  mounted() {
    this.win = window
    this.container = this.$refs.container
    this.container.style.minHeight = `${window.innerHeight - (52 + 65 + 12)}px`
    // 节流监听窗口大小
    this.handler = throttle(this.resize, 800)
    this.win.addEventListener('resize', this.handler)
    //当设置完container宽高后获取数据
    this.isShow=true
    setTimeout(()=>{
      this.data = data
      if (this.data){
        this.isShow = false
        this.toggleExpand(this.data, true)
      }
    },3000)
    //默认展开所有节点

  },
  methods: {
    // 监听自定义事件
    resize() {
      this.container.style.minHeight = `${window.innerHeight - (52 + 65 + 24)}px`
      if (typeof this.win !== 'undefined') {
        if (!this.container || !this.width || !this.height) return
      }
    },
    //渲染节点
    renderContent(h, data) {
      // 通过data中的className属性来对div元素进行注入class
      // 每个节点渲染必然会走这个函数
      //这里对应的不同的className 需要在上面的tree.less中写入样式
      const result = this.changeRender(data)
      return result
    },
    //判断相对等级渲染的dom
    changeRender(data) {
      if ([0, 1, '0', '1'].includes(data.level)) {
        data.img = img
        data.className = className
        return (
          <div style='position:relative;display:flex;align-items: center;'>
            <div
              style='position: absolute;display: flex;width: 72px;height: 72px;border-radius: 50%;justify-content: center;align-items: center;'>
              <img
                src={(data.level == 0 && data.type == 0) ? data.img.userUrl : (data.level == 1 && data.type == 1) ? data.img.teacherUrl : (data.level == 1 && data.type == 2) ? data.img.adminUrl : (data.level == 1 && data.type == 3) ? data.img.directorUrl : data.img.postUrl} />
            </div>
            <div
              class={(data.level == 0 && data.type == 0) ? data.className.user : (data.level == 1 && data.type == 1) ? data.className.teacher : (data.level == 1 && data.type == 2) ? data.className.admin : (data.level == 1 && data.type == 3) ? data.className.director : data.className.post}
            >
              <span>
                {data.label}
              </span>
              <span style='font-size: 11px;'>{data.jobNum}</span>
            </div>
          </div>
        )
      }
      if ([2, '2'].includes(data.level)) {
        data.img = img
        data.className = className

        return (
          <div style='position:relative;display:flex;align-items: center;'>

            <div
              class={data.type == 1 ? data.className.teacherChild : data.type == 2 ? data.className.adminChild : data.type == 3 ? data.className.directorChild : data.className.postChild}
            >{data.label}
            </div>
          </div>
        )
      }
      if ([3, '3'].includes(data.level)) {
        data.className = className
        return (
          <div style='position:relative;display:flex;align-items: center;'>
            <div
              class={data.type == 1 ? data.className.tecListChild : data.type == 2 ? data.className.adminListChild : data.type == 3 ? data.className.directorListChild : data.className.postListChild}
            >
              <div
                style='width: 24px;height: 24px;border-radius: 50%;color: #fff;display: block;justify-content: normal;align-items: normal;padding: 0;min-width: 0;font-weight: normal;'
                class={data.type == 1 ? data.className.teacherChild : data.type == 2 ? data.className.adminChild : data.type == 3 ? data.className.directorChild : data.className.postChild}>
                <i
                  class={data.mark == 'app' ? 'action-icon actionzhuomianyingyongshezhi' : data.mark == 'server' ? 'action-icon actionfuwu1' : 'action-icon actioncaidan'}
                  style=''></i>
              </div>
              <div style='display: flex;flex: 1;justify-content: center;height: 24px;'>{data.label}</div>
            </div>
          </div>
        )
      } else {
        data.className = className
        return (
          <div style='position:relative;display:flex;align-items: center;'>
            <div
              style='display: flex;justify-content: center;align-items: center;font-weight: 400;min-width: 90px;font-size: 13px;'
              class={data.type == 1 ? data.className.tecListChild : data.type == 2 ? data.className.adminListChild : data.type == 3 ? data.className.directorListChild : data.className.postListChild}>
              {data.label}
            </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)
      }
    }
  },
  beforeDestroy() {
    this.win.removeEventListener('resize', this.handler)
  }
}
</script>


<style scoped lang='less'>
@import "./index";
</style>
  1. node.js文件
// 判断是否叶子节点
const isLeaf = (data, prop) => {
    return !(Array.isArray(data[prop]) && data[prop].length > 0)
}
// console.info('Thank you for using vue-tree-color \nIf you have any questions about this plug-in, please contact me in the following ways \nWeChat: yanjiahui12345 \nWeChat official number: Web_Miao')
// 创建 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')
    }

    childNodes.push(renderLabel(h, data, context))

    if (!props.collapsable || data[props.props.expand]) {
        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) {
        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

    return renderNode(h, props.data, context)
}

export default render
在页面中使用
  1. 创建新页面,在页面中引入需要用到的数据(自己提出公共数据,根据项目需求创建),引入样式(根据项目需求创建)
创建permissionMap.vue
<template>
  <div ref='container' class='container'>
    <div v-if='isShow' class='spin'>
      <a-spin tip='数据加载中...' />
    </div>
    <vueOrgTree
      v-if='!isShow'
      class='vueOrgTree'
      :data='data'
      :horizontal='true'
      :collapsable='true'
      @on-expand='onExpand'
      @on-node-click='NodeClick'
      @on-node-mouseover='onMouseover'
      @on-node-mouseout='onMouseout'
      :renderContent='renderContent'


    />
  </div>
</template>
<script>
import vueOrgTree from './Components/vueOrgTree.vue'
import throttle from 'lodash/throttle'
//导入数据
import { data, img, className } from './index'

export default {
  name: 'permissionMap',
  components: {
    vueOrgTree
  },
  data() {
    return {
      win: '',
      container: '',
      handler: () => {
      },
      isShow: true,
      data: {}

    }
  },
  created() {

  },
  mounted() {
    this.win = window
    this.container = this.$refs.container
    this.container.style.minHeight = `${window.innerHeight - (52 + 65 + 12)}px`
    // 节流监听窗口大小
    this.handler = throttle(this.resize, 800)
    this.win.addEventListener('resize', this.handler)
    //当设置完container宽高后获取数据
    this.isShow=true
    setTimeout(()=>{
      this.data = data
      if (this.data){
        this.isShow = false
        this.toggleExpand(this.data, true)
      }
    },3000)
    //默认展开所有节点

  },
  methods: {
    // 监听自定义事件
    resize() {
      this.container.style.minHeight = `${window.innerHeight - (52 + 65 + 24)}px`
      if (typeof this.win !== 'undefined') {
        if (!this.container || !this.width || !this.height) return
      }
    },
    //渲染节点
    renderContent(h, data) {
      // 通过data中的className属性来对div元素进行注入class
      // 每个节点渲染必然会走这个函数
      //这里对应的不同的className 需要在上面的tree.less中写入样式
      const result = this.changeRender(data)
      return result
    },
    //判断相对等级渲染的dom
    changeRender(data) {
      if ([0, 1, '0', '1'].includes(data.level)) {
        data.img = img
        data.className = className
        return (
          <div style='position:relative;display:flex;align-items: center;'>
            <div
              style='position: absolute;display: flex;width: 72px;height: 72px;border-radius: 50%;justify-content: center;align-items: center;'>
              <img
                src={(data.level == 0 && data.type == 0) ? data.img.userUrl : (data.level == 1 && data.type == 1) ? data.img.teacherUrl : (data.level == 1 && data.type == 2) ? data.img.adminUrl : (data.level == 1 && data.type == 3) ? data.img.directorUrl : data.img.postUrl} />
            </div>
            <div
              class={(data.level == 0 && data.type == 0) ? data.className.user : (data.level == 1 && data.type == 1) ? data.className.teacher : (data.level == 1 && data.type == 2) ? data.className.admin : (data.level == 1 && data.type == 3) ? data.className.director : data.className.post}
            >
              <span>
                {data.label}
              </span>
              <span style='font-size: 11px;'>{data.jobNum}</span>
            </div>
          </div>
        )
      }
      if ([2, '2'].includes(data.level)) {
        data.img = img
        data.className = className

        return (
          <div style='position:relative;display:flex;align-items: center;'>

            <div
              class={data.type == 1 ? data.className.teacherChild : data.type == 2 ? data.className.adminChild : data.type == 3 ? data.className.directorChild : data.className.postChild}
            >{data.label}
            </div>
          </div>
        )
      }
      if ([3, '3'].includes(data.level)) {
        data.className = className
        return (
          <div style='position:relative;display:flex;align-items: center;'>
            <div
              class={data.type == 1 ? data.className.tecListChild : data.type == 2 ? data.className.adminListChild : data.type == 3 ? data.className.directorListChild : data.className.postListChild}
            >
              <div
                style='width: 24px;height: 24px;border-radius: 50%;color: #fff;display: block;justify-content: normal;align-items: normal;padding: 0;min-width: 0;font-weight: normal;'
                class={data.type == 1 ? data.className.teacherChild : data.type == 2 ? data.className.adminChild : data.type == 3 ? data.className.directorChild : data.className.postChild}>
                <i
                  class={data.mark == 'app' ? 'action-icon actionzhuomianyingyongshezhi' : data.mark == 'server' ? 'action-icon actionfuwu1' : 'action-icon actioncaidan'}
                  style=''></i>
              </div>
              <div style='display: flex;flex: 1;justify-content: center;height: 24px;'>{data.label}</div>
            </div>
          </div>
        )
      } else {
        data.className = className
        return (
          <div style='position:relative;display:flex;align-items: center;'>
            <div
              style='display: flex;justify-content: center;align-items: center;font-weight: 400;min-width: 90px;font-size: 13px;'
              class={data.type == 1 ? data.className.tecListChild : data.type == 2 ? data.className.adminListChild : data.type == 3 ? data.className.directorListChild : data.className.postListChild}>
              {data.label}
            </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)
      }
    }
  },
  beforeDestroy() {
    this.win.removeEventListener('resize', this.handler)
  }
}
</script>


<style scoped lang='less'>
@import "./index";
</style>
index.js公共数据(根据项目需求看是否提取)
//数据
export const data = {
  id: 0,
  label: '用户角色',
  level: 0,//层级
  type: 0,//类型(教师以及下面的子集为同一类型)
  jobNum: '123456',
  children: [
    {
      id: 1,
      label: '教师',
      level: 1,
      type: 1,
      jobNum: '角色',
      children: [
        {
          id: 55,
          level: 2,
          type: 1,
          label: '办事大厅',
          children: [
            {
              id: 61,
              level: 3,
              type: 1,
              label: '应用',
              mark: 'app',//应用层标识
              children: [
                {
                  id: 63,
                  level: 4,
                  type: 1,
                  label: '信息站群',
                  children: [
                    {
                      id: 63,
                      level: 5,
                      type: 1,
                      label: '信息站群1'
                    },
                    {
                      id: 63,
                      level: 5,
                      type: 1,
                      label: '信息站群2'
                    }
                  ]

                },
                {
                  id: 63,
                  level: 4,
                  type: 1,
                  label: 'AI助教'
                }
              ]
            },
            {
              id: 62,
              level: 3,
              type: 1,
              label: '服务',
              mark: 'server'//服务层标识
            },
            {
              id: 63,
              level: 3,
              type: 1,
              label: '菜单',
              mark: 'menu'//菜单层标识
            }
          ]
        },
        {
          id: 56,
          level: 2,
          type: 1,
          label: '离校系统',
          children: [
            {
              id: 61,
              level: 3,
              type: 1,
              label: '应用'
            },
            {
              id: 62,
              level: 3,
              type: 1,
              label: '服务'
            },
            {
              id: 63,
              level: 3,
              type: 1,
              label: '菜单'
            }
          ]
        },
        {
          id: 57,
          level: 2,
          type: 1,
          label: '用户中心',
          children: [
            {
              id: 61,
              level: 3,
              type: 1,
              label: '应用'
            },
            {
              id: 62,
              level: 3,
              type: 1,
              label: '服务'
            },
            {
              id: 63,
              level: 3,
              type: 1,
              label: '菜单'
            }
          ]
        }
      ]
    },
    {
      id: 2,
      label: '管理员',
      level: 1,
      type: 2,
      jobNum: '角色',
      children: [
        {
          id: 55,
          level: 2,
          type: 2,
          label: '办事大厅',
          children: [
            {
              id: 61,
              level: 3,
              type: 2,
              label: '应用',
              children: [
                {
                  id: 63,
                  level: 4,
                  type: 2,
                  label: '信息站群',
                  children: [
                    {
                      id: 63,
                      level: 5,
                      type: 2,
                      label: '信息站群1'
                    },
                    {
                      id: 63,
                      level: 5,
                      type: 2,
                      label: '信息站群2'
                    }
                  ]
                },
                {
                  id: 63,
                  level: 4,
                  type: 2,
                  label: 'AI助教'
                }
              ]
            },
            {
              id: 62,
              level: 3,
              type: 2,
              label: '服务'
            },
            {
              id: 63,
              level: 3,
              type: 2,
              label: '菜单'
            }
          ]
        },
        {
          id: 56,
          level: 2,
          type: 2,
          label: '离校系统'
        },
        {
          id: 57,
          level: 2,
          type: 2,
          label: '用户中心'
        }

      ]

    },
    {
      id: 3,
      label: '学院主任',
      level: 1,
      type: 3,
      jobNum: '角色',
      children: [
        {
          id: 58,
          level: 2,
          type: 3,
          label: '办事大厅',
          children: [
            {
              id: 61,
              level: 3,
              type: 3,
              label: '应用',
              children: [
                {
                  id: 63,
                  level: 4,
                  type: 3,
                  label: '信息站群',
                  children: [
                    {
                      id: 63,
                      level: 5,
                      type: 3,
                      label: '信息站群1'
                    },
                    {
                      id: 63,
                      level: 5,
                      type: 3,
                      label: '信息站群2'
                    }
                  ]
                },
                {
                  id: 63,
                  level: 4,
                  type: 3,
                  label: 'AI助教'
                }
              ]
            },
            {
              id: 62,
              level: 3,
              type: 3,
              label: '服务'
            },
            {
              id: 63,
              level: 3,
              type: 3,
              label: '菜单'
            }
          ]
        },
        {
          id: 59,
          level: 2,
          type: 3,
          label: '离校系统'
        },
        {
          id: 60,
          level: 2,
          type: 3,
          label: '用户中心'
        }

      ]
    },
    {
      id: 4,
      label: '岗位',
      level: 1,
      type: 4,
      jobNum: '角色',
      children: [
        {
          id: 61,
          level: 2,
          type: 4,
          label: '办事大厅',
          children: [
            {
              id: 61,
              level: 3,
              type: 4,
              label: '应用',
              children: [
                {
                  id: 63,
                  level: 4,
                  type: 4,
                  label: '信息站群'
                  ,
                  children: [
                    {
                      id: 63,
                      level: 5,
                      type: 4,
                      label: '信息站群1'
                    },
                    {
                      id: 63,
                      level: 5,
                      type: 4,
                      label: '信息站群2'
                    }
                  ]
                },
                {
                  id: 63,
                  level: 4,
                  type: 4,
                  label: 'AI助教'
                }
              ]
            },
            {
              id: 62,
              level: 3,
              type: 4,
              label: '服务'
            },
            {
              id: 63,
              level: 3,
              type: 4,
              label: '菜单'
            }
          ]
        },
        {
          id: 62,
          level: 2,
          type: 4,
          label: '离校系统'
        },
        {
          id: 63,
          level: 2,
          type: 4,
          label: '用户中心'
        }
      ]
    }
  ]
}
//前两级渲染时需要的图片
export const img = {
  userUrl: require('@assets/permissionMap/user.png'),//用户角色
  teacherUrl: require('@assets/permissionMap/teacher.png'),//教师
  adminUrl: require('@assets/permissionMap/administrators.png'),//管理员
  directorUrl: require('@assets/permissionMap/director.png'),//学院主任
  postUrl: require('@assets/permissionMap/post.png')//岗位
}
//渲染中所需要的样式
export const className = {
  user: 'userBgc',//用户角色层样式
  teacher: 'teacherBgc',//教师样式
  admin: 'adminBgc',//管理员层样式
  director: 'directorBgc',//学院主任样式
  post: 'postBgc',//岗位样式
  sys: 'sysStyle',//系统层样式
  teacherChild: 'teaC',//教师子集样式
  adminChild: 'adminC',//管理员子集样式
  directorChild: 'directC',//学院主任样式子集
  postChild: 'postC',//岗位样式子集
  tecListChild: 'tecListC',//教师应用,服务,菜单样式
  adminListChild: 'adminListC',//管理员应用,服务,菜单样式
  directorListChild: 'directorListC',//学院主任应用,服务,菜单样式
  postListChild: 'postListC'//岗位应用,服务,菜单样式
}

index.less样式
.container {
  display: flex;

  .vueOrgTree {
    display: flex;
    flex: 1;
    align-items: center;
    box-sizing: border-box;
    overflow: auto;
  }

  .spin {
    display: flex;
    flex: 1;
    justify-content: center;
    align-items: center;
    background: #fff;
  }
}

.userBgc, .teacherBgc, .adminBgc, .directorBgc, .postBgc, .sysStyle {
  display: flex;
  //justify-content: center;
  align-items: center;
  flex-direction: column;
  padding: 0px 15px 0px 74px;
  min-width: 157px;
  height: 50px;
  box-shadow: 0px 0px 13px 0px rgba(0, 0, 0, 0.09);
  border-radius: 31px !important;
  border: 4px solid #FFFFFF;
  font-size: 16px;
  font-weight: bold;
  color: #FFFFFF;
}

.teaC, .adminC, .directC, .postC {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 10px 20px;
  min-width: 80px;
  height: 30px;
  border-radius: 16px;
  font-size: 13px;
  font-weight: bold;
  color: #FFFFFF;
}

.userBgc {
  background: #88A3C7;
}

.teacherBgc, .teaC {
  background: #FFA66F;
}

.adminBgc, .adminC {
  background: #7BA1FF;
}

.directorBgc, .directC {
  background: #A382EF;
}

.postBgc, .postC {
  background: #60CFD9;
}

.tecListC, .adminListC, .directorListC, .postListC {
  display: flex;
  justify-content: space-between;
  align-items: center;
  min-width: 80px;
  height: 30px;
  border-radius: 21px;
  border: 1px solid #FFA66F;
  padding: 3px;
  font-size: 14px;
  font-weight: 500;
  color: #333333;
}

.tecListC {
  border: 1px solid #FFA66F;
}

.adminListC {

  border: 1px solid #7BA1FF;
}

.directorListC {

  border: 1px solid #A382EF;
}

.postListC {

  border: 1px solid #60CFD9;
}

  • 8
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
实现动态组织架构图,可以考虑使用 Vue.js 和一些可视化库(例如 D3.js 或 ECharts)来创建可交互的图表。 以下是一个简单的示例: 首先,可以使用 Vue.js 创建一个组件来显示组织架构图。在组件,可以定义一个数据对象来存储组织架构的节点和链接信息。可以通过组件的 props 来传递数据。 然后,可以使用可视化库来绘制组织架构图。可以使用 D3.js 或 ECharts 来绘制节点和链接,并添加事件监听器来支持交互。 最后,可以在组件添加逻辑来更新组织架构图。例如,可以从后端API获取数据,并将其传递给组件以更新图表。 以下是一个简单的 Vue.js 组件示例: ```html <template> <div class="org-chart"></div> </template> <script> import * as d3 from 'd3'; export default { name: 'OrgChart', props: { data: { type: Object, required: true } }, mounted() { this.drawChart(); }, methods: { drawChart() { const { nodes, links } = this.data; const svg = d3.select('.org-chart'); const width = +svg.attr('width'); const height = +svg.attr('height'); const simulation = d3.forceSimulation(nodes) .force('link', d3.forceLink(links).id(d => d.id)) .force('charge', d3.forceManyBody()) .force('center', d3.forceCenter(width / 2, height / 2)); const link = svg.append('g') .attr('class', 'links') .selectAll('line') .data(links) .enter() .append('line') .attr('stroke-width', d => Math.sqrt(d.value)); const node = svg.append('g') .attr('class', 'nodes') .selectAll('circle') .data(nodes) .enter() .append('circle') .attr('r', 5) .attr('fill', '#1f77b4') .call(d3.drag() .on('start', dragstarted) .on('drag', dragged) .on('end', dragended)); node.append('title') .text(d => d.id); simulation.on('tick', () => { link .attr('x1', d => d.source.x) .attr('y1', d => d.source.y) .attr('x2', d => d.target.x) .attr('y2', d => d.target.y); node .attr('cx', d => d.x) .attr('cy', d => d.y); }); function dragstarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } function dragged(d) { d.fx = d3.event.x; d.fy = d3.event.y; } function dragended(d) { if (!d3.event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; } } } }; </script> ``` 在这个示例,我们使用了 D3.js 来绘制组织架构图。在组件加载后,我们调用了 `drawChart` 方法来生成图表。该方法使用 D3.js 创建一个力导向图,并为每个节点和链接添加了一个圆和一条线。我们还添加了一个事件监听器来支持拖拽操作。 然后,我们可以在父组件使用该组件并传递数据: ```html <template> <org-chart :data="chartData"></org-chart> </template> <script> import OrgChart from './OrgChart.vue'; export default { name: 'App', components: { OrgChart }, data() { return { chartData: { nodes: [ { id: 'CEO' }, { id: 'CTO' }, { id: 'CFO' }, { id: 'Marketing Director' }, { id: 'Sales Director' } ], links: [ { source: 'CEO', target: 'CTO', value: 1 }, { source: 'CEO', target: 'CFO', value: 1 }, { source: 'CEO', target: 'Marketing Director', value: 1 }, { source: 'CEO', target: 'Sales Director', value: 1 } ] } } } }; </script> ``` 在这个示例,我们将组织架构的节点和链接信息存储在 `chartData` 对象,并将其传递给 `OrgChart` 组件组件会根据数据来生成图表。您可以根据自己的需求修改和扩展这个示例。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值