Нарисуйте диаграммы топологии самым простым способом! ! !

React.js

git-адрес

предисловие

Некоторое время назад я рефакторил следующую страницу (страница товара неудобна для скриншотов):

Подобно конфигурации карты топологии, оригинал должен использоватьgo.jsРеализовано, подобные библиотеки имеютantv g6. Основная цель рефакторинга — улучшение качества кода и снижение затрат на обслуживание.Продукт нуждается в более сильных возможностях кастомизации (чтобы справляться с нестандартными потребностями продакт-менеджеров), поэтому после некоторых исследований мы окончательно решили отказаться от использования готовых библиотек. . Причины следующие:

  1. Высокая стоимость обслуживания: аналогичные библиотеки (antv/g6, go.js) реализованы на основе canvas, и все они определяют набор компонентов одинаково, что требует определенных затрат на обучение. на таких библиотеках относительно сложно.
  2. Плохая гибкость: поскольку это реализуется холстом, элементы обычно должны указывать размер, поэтому нет элементов DOM, которые легко реализовать в некоторых местах, где размер элемента должен быть адаптивным;
  3. Плохая возможность настройки. Можно использовать только те API и события, которые определены в библиотеке, и ничего нельзя сделать, если возникают экстремальные требования.

Конечно, две вышеупомянутые библиотеки все еще достаточно мощные, но по этим причинам я создаю библиотеку инструментов для настройки карты топологии на основе (DOM + SVG).topology-byfe

демо

исходный код

import React from 'react';
import { Topology, topologyWrapper, TemplateWrapper } from 'topology-byfe';
import { ITopologyNode, ITopologyData, IWrapperOptions } from 'topology-byfe/lib/declare';
import './index.less';

interface FlowState {
    data: ITopologyData;
}
class Flow extends React.Component<{}, FlowState> {
    state: FlowState = {
        data: { lines: [], nodes: [] },
    };

    generatorNodeData = (isBig: boolean) => ({
        id: `${Date.now()}`,
        name: isBig ? '宽节点' : '窄节点',
        content: isBig ? '这是一个宽节点' : '这是一个窄节点',
        branches: isBig ? ['锚点1', '锚点2', '锚点3'] : ['锚点1'],
    });

    handleSelect = (data: ITopologyData) => {
        console.log(data);
    }

    renderTreeNode = (data: ITopologyNode, { anchorDecorator }: IWrapperOptions) => {
        const {
            name = '',
            content = '',
            branches = [],
        } = data;
        return (
            <div className="topology-node">
                <div className="node-header">{name}</div>
                <p className="node-content">{content}</p>
                {branches.length > 0 && (
                    <div className="flow-node-branches-wrapper">
                        {branches.map(
                            (item: string, index: number) => anchorDecorator({
                                anchorId: `${index}`,
                            })(<div className="flow-node-branch">{item}</div>),
                        )}
                    </div>
                )}
            </div>
        );
    };

    onChange = (data: ITopologyData, type: string) => {
        this.setState({ data });
        console.log('change type:', type);
    };

    render() {
        const { data } = this.state;
        return (
            <div className="topology">
                <div className="topology-templates">
                    <TemplateWrapper generator={() => this.generatorNodeData(true)}>
                        <div className="topology-templates-item">宽节点</div>
                    </TemplateWrapper>
                    <TemplateWrapper generator={() => this.generatorNodeData(false)}>
                        <div className="topology-templates-item">窄节点</div>
                    </TemplateWrapper>
                </div>
                <div style={{ width: '100%', height: 800 }}>
                    <Topology
                        data={data}
                        autoLayout
                        onChange={this.onChange}
                        onSelect={this.handleSelect}
                        renderTreeNode={this.renderTreeNode}
                    />
                </div>
            </div>
        );
    }
}

export default topologyWrapper(Flow);

визуализация

Видно, что в пакете предоставлено всего несколько API, а документы можно прочитать за несколько минут.Причина, по которой их мало, заключается в том, что библиотека отвечает только за размещение узлов в правильном положении, и это хорошо подключить их онлайн. Ответственный», чтобы изменить стиль, вы можете добавить класс в свой div, добавить событие, а затем перейти к onXXX, делать все, что хотите.

Почему стоит выбрать DOM + SVG?

Основная причина в "простоте"! Для карты топологии, подобной этой Если вы хотите нарисовать простой узел, вам нужно всего несколько строк кода, чтобы реализовать его в DOM. Если вы используете холст, вам придется много писать. Другие думают, что это «много всего». В то же время, вместо того, чтобы сначала изучать набор компонентов, а затем использовать эти «странные» API для написания взаимодействий и стилей, разве не было бы очень приятно начать использовать div + css, с которым вы больше всего знакомы? ? Для разработки отладка DOM может видеть детали каждого элемента, но холст ничего не может сделать.

как использовать

Код основной части:

 <Topology
    data={data}
    autoLayout
    onChange={this.onChange}
    onSelect={this.handleSelect}
    renderTreeNode={this.renderTreeNode}
 />

data

data = {
    nodes: [
        { id: '1', position: { x: 0, y: 0 } },
        { id: '2', position: { x: 100, y: 100 } }
    ],
    lines: [
        { start: '1-0', end: '2' }
    ]
}

data.nodes

данные содержат два атрибута: узлы и линии, узлы записывают информацию об узле, каждый узел содержит атрибуты id и position, идентификатор является обязательным, position записывает положение узла, если он не содержит положение, нажмите Auto Layout, он будет сгенерирован автоматически .

data.lines

строки записывают отношения между узлами, начало записывает информацию о начальной точке, формат: «идентификатор начальной точки-идентификатор точки привязки», конец записывает информацию о конечной точке, формат: «идентификатор конечной точки».

autoLayout

Вышеуказанный эффект можно увидеть на фиг. Значок в нижнем праве в правом нижнем углу вторую функцию автоматической макеты, для удобства макета, макет автоматически рассчитывается на основе положения дерева структуры узлов. Когда данные начальных полей не являются положением, если autolayout true, компонент автоматически запускает функциональный макет, эквивалент нажатия кнопки автоматической макета.

onChange

Компонент использует что-то вроде ввода или выбора. Когда возникает новый узел или соединение, запускается onChange. OnChange имеет два параметра, newData и changeType. Весь процесс полностью контролируется. Вы можете выполнить некоторую проверку в onChange, чтобы определить данные. обновить.

onSelect

Запускается при выборе узла или сегмента линии, параметр selectData имеет тот же формат, что и данные.

renderTreeNode

renderTreeNode принимает два параметра: nodeData, декораторы и возвращает DOM узла.

renderTreeNode = (data: ITopologyNode, { anchorDecorator }) => {
        // name、content、branches都是自定义的字段,通过模板节点生成,详见TemplateWrapper
        const {
            name = '',
            content = '',
            branches = [],
        } = data;
        return (
            <div className="topology-node">
                <div className="node-header">{name}</div>
                <p className="node-content">{content}</p>
                {branches.length > 0 && (
                    <div className="flow-node-branches-wrapper">
                        {branches.map(
                            (item: string, index: number) => anchorDecorator({
                                anchorId: `${index}`,
                            })(<div className="flow-node-branch">{item}</div>),
                        )}
                    </div>
                )}
            </div>
        );
    };

Якорь, декораторы.anchorDecorator

anchorDecorator({ anchorId: `${index}` })(
    <div className="flow-node-branch">
        {item}
    </div>
)

AnchorDecorator — это функция-декоратор, которая принимает параметр. В настоящее время она содержит только атрибут anchorId, то есть идентификатор привязки. Если он не передан, будет автоматически сгенерирован внутренний идентификатор с автоматическим приращением. Как видите, как выглядит якорь и где он расположен, зависит только от вас.

templateWrapper

<div className="topology-templates">
        <TemplateWrapper generator={() => this.generatorNodeData(true)}>
            <div className="topology-templates-item">宽节点</div>
        </TemplateWrapper>
        <TemplateWrapper generator={() => this.generatorNodeData(false)}>
            <div className="topology-templates-item">窄节点</div>
        </TemplateWrapper>
</div>

Сгенерировать ноду шаблона через упаковку templateWrapper, получить функцию генератора, при добавлении ноды он вызовет эту функцию для генерации исходных данных ноды, какое значение в ней содержится решать вам, но она должна содержать уникальный id стоимость.

topologyWrapper

export default topologyWrapper(Flow);

Компонент верхнего уровня, содержащий часть перетаскивания, должен быть обернут с помощью topologyWrapper. Это связано с тем, что при использовании react-dnd вам необходимо установить серверную часть. Вот простой экспорт для простоты использования:

export const topologyWrapper = DragDropContext(HTML5BackEnd);

Суммировать

С точки зрения требований он должен иметь лучшие возможности настройки и гибкость, а с точки зрения рефакторинга необходимо сделать код проще и понятнее. go.js очень мощный, но для относительно простой сцены, такой как карта топологии, он не требует стольких сложных возможностей. Вместо этого могут возникнуть вышеупомянутые проблемы. Все реализации холста должны иметь схожие проблемы. Таким образом, при полном использовании возможностей DOM эта библиотека может быть лучшим выбором, если требования могут быть выполнены.

наконец

В проекте реализованы только простые функции, и есть недостатки.Добро пожаловать в выпуск и пр. Кроме того, наша компания набирает сотрудников, приглашаем присоединиться:Ссылка для набора! ! !