Компонент организационного дерева на основе Vue

Архитектура внешний интерфейс GitHub Vue.js
Компонент организационного дерева на основе Vue

В связи с потребностями бизнеса компании необходимо разработать древовидный компонент, отображающий организационную структуру (проект компании основан на Vue). После долгих поисков на GitHub таких компонентов не так много, и нет компонентов, отвечающих потребностям бизнеса, поэтому я решил собрать свои собственные колеса!

анализировать

  • Поскольку это дерево, каждый узел должен быть одним и тем же компонентом.
  • Узел устанавливается ниже узла, поэтому компонент узла должен бытьРекуррентный компонент

Ну вот и проблема. Как писать рекурсивные компоненты?

рекурсивный компонент

Официальная документация VueВот что он говорит:

Компонент может рекурсивно вызывать себя в пределах своего шаблона. Однако это возможно только в том случае, если у него есть опция имени

Далее напишем рекурсивный компонент узла дерева:

<template>
    <div class="org-tree-node">
    	<div class="org-tree-node-label">{{data.label}}</div>
    	
    	<div class="org-tree-node-children" v-if="data.children">
    	    <org-tree-node v-for="node in data.children" :data="node" :key="data.id"></org-tree-node>
    	</div>
    </div>
</template>

<script>
    export default {
    	name: 'OrgTreeNode',
    	props: {
    	    data: Object
    	}
    }
</script>

<style>
    /* ... */
</style>

Затем визуализируйте этот компонент, эффект будет следующим

На данный момент простой компонент организационного дерева завершен.

Однако дела еще далеки от завершения. . .

Требования: Метка узла должна поддерживать настройку, а дерево должно поддерживать горизонтальное отображение!

Поэтому мы модифицируем рекурсивный компонент следующим образом:

<template>
    <div class="org-tree-node">
    	<div class="org-tree-node-label">
    	    <slot>{{data.label}}</slot>
    	</div>
    	
    	<div class="org-tree-node-children" v-if="data.children">
    	    <org-tree-node v-for="node in data.children" :data="node" :key="data.id"></org-tree-node>
    	</div>
    </div>
</template>

<script>
    export default {
    	name: 'OrgTreeNode',
    	props: {
    	    data: Object
    	}
    }
</script>

<style>
    /* ... */
</style>

Мы используем слот-слоты для поддержки настраиваемости меток, но здесь возникает проблема: мы обнаружили, что только метки узлов на первом уровне могут быть настроены, а вложенные дочерние узлы не могут эффективно передавать слоты-слоты. Я долго проверял интернет, но все равно никаких результатов, поэтому посмотрел официальную документацию. найтифункциональные компоненты.由于之前使用过element-uiизtreeкомпонент, вдохновленный идеей, что он может быть какelement-uiизtreeПередайте компонент, напримерrenderContentФункция, вызывающая сторона сама отображает метку узла, чтобы достичь цели настройки узла!

функциональные компоненты

Затем мы преобразуем компонент шаблона узла дерева в функциональный компонент. Напишите node.js:

  1. Сначала мы реализуем функцию рендеринга

    export const render = (h, context) => {
      const {props} = context
      return renderNode(h, props.data, context)
    }
    
  2. Реализовать функцию renderNode

    export const renderNode = (h, data, context) => {
      const {props} = context
      const childNodes = []
    
      childNodes.push(renderLabel(h, data, context))
    
      if (props.data.children && props.data.children.length) {
        childNodes.push(renderChildren(h, props.data.children, context))
      }
    
      return h('div', {
        domProps: {
          className: 'org-tree-node'
        }
      }, childNodes)
    }
    
  3. Реализуйте функцию renderLabel. Ключ к настройке метки узла находится здесь:

    export const renderLabel = (h, data, context) => {
      const {props} = context
      const renderContent = props.renderContent
      const childNodes = []
    
      // 节点label定制,由调用者传入的renderContent实现
      if (typeof renderContent === 'function') {
        let vnode = renderContent(h, props.data)
    
        vnode && childNodes.push(vnode)
      } else {
        childNodes.push(props.data.label)
      }
    
      return h('div', {
        domProps: {
          className: 'org-tree-node-label'
        }
      }, childNodes)
    }
    
  4. Реализуйте функцию renderChildren. Здесь рекурсивно вызывается renderNode, который реализует рекурсивный компонент

    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 ''
    }
    

На этом наша функция рендеринга завершена, а затем используйте функцию рендеринга для определения функциональных компонентов. В компоненте дерева объявите:

<template>
    <!-- ... -->
</template>

<script>
    import render from './node.js'
    
    export default {
        name: 'OrgTree',
        components: {
            OrgTreeNode: {
                render,
                // 定义函数式组件
                functional: true
            }
        }
    }
</script>

На этом преобразование нашего функционального компонента завершено, и горизонтальным отображением можно управлять с помощью стиля.

CSS-стиль

Стили предварительно компилируются с помощью less. Линии между узлами:before,:afterпсевдоэлементborderрисовать

расширение функции

  • ДобавленlabelClassNameАтрибут для поддержки настройки стиля меток узлов
  • ДобавленlabelWidthАтрибут для ограничения ширины метки узла
  • Добавленpropsатрибут, ссылкаelement-uiизtreeРеквизиты компонентов для поддержки сложных структур данных
  • ДобавленcollapsableАтрибуты для поддержки расширения и свертывания дочерних узлов (операции развертывания и свертывания должны быть реализованы вызывающей стороной)

только начал использоватьflexМакет, но для совместимости с IE9, позже был изменен наdisplay: tableмакет

окончательный эффект

  • default

  • horizontal

краткое изложение проблемы

  • Хранилище дерева может быть определено для хранения состояния каждого узла, так что расширяемые и сворачиваемые состояния узлов дерева могут поддерживаться внутри.

Наконец, прикрепите исходный код портала:GitHub.com/Ху Кайбайху/… !

использованная литература

GitHub.com/H Игорь Сильва Р…