index.tsx 7.92 KB
import React, { memo, useContext, useEffect, useState } from 'react';
import { BuilderContext } from '@qx/flow';
import { Drawer, Spin, Tabs } from 'antd';
import NodeList, { NodeItemsProps } from './components/node-list';
import { useSearchParams } from '@/hooks';
import { getListGroupNode } from '@/services';
import { QxIcon } from '@/components';
import { ReactSortable } from 'react-sortablejs';
import { cloneDeep } from 'lodash-es';
import { v4 as uuid } from 'uuid';
// @ts-ignore
import type { INodePanelProps } from '@qx/flow';
import Empty from './img/empty.png';

import './index.less';

const prefix = 'qx-flow-node-panel';

const panelTabs = [
  {
    name: '系统节点',
    key: 'SYSTEM',
  },
  {
    name: '扩展节点',
    key: 'EXTEND',
  },
  {
    name: '开发节点',
    key: 'DEVELOP',
  },
];

const NodePanel: React.FC<NodePanelProps> = (props) => {
  // @ts-ignore
  const { visible, onClose } = props;
  const [query] = useSearchParams<any>();
  const appId = query?.appId;
  const { registerNodes } = useContext(BuilderContext);
  const [loading, setLoading] = useState(false);
  const [activeKey, setActiveKey] = useState('SYSTEM');
  const [isManage, setIsManage] = useState<boolean>(false);
  const [nodeList, setNodeList] = useState<NodeItemsProps[]>([]);
  const [originalNodeList, setOriginalNodeList] = useState<NodeItemsProps[]>(
    [],
  );
  const [nodeEdit, setNodeEdit] = useState(false);
  const [nameList, setNameList] = useState<string[]>([]);
  // console.log('registerNodes', registerNodes, props);

  useEffect(() => {
    if (!isManage) return;
    if (!nodeList?.length) {
      setNameList([]);
    } else {
      const _list: string[] = [];
      nodeList.forEach((el) => {
        if (el.name.includes('未命名分组(')) {
          _list.push(el.name);
        }
      });
      setNameList(_list);
    }
  }, [JSON.stringify(nodeList), isManage]);

  useEffect(() => {
    setIsManage(false);
    setNameList([]);
    if (!appId || !visible) return;
    setLoading(true);
    // TODO
    getListGroupNode(appId, {
      type: activeKey,
    })
      .then((data: NodeItemsProps[]) => {
        setLoading(false);
        if (data?.length) {
          setOriginalNodeList(cloneDeep(data));
          setNodeList(data.filter((el) => !el.disabled) || []);
          return;
        }
        setNodeList([]);
        setOriginalNodeList([]);
      })
      .catch(() => {
        setLoading(false);
        setNodeList([]);
        setOriginalNodeList([]);
      });
  }, [activeKey, visible]);

  useEffect(() => {
    if (!visible) {
      setActiveKey('SYSTEM');
    }
  }, [visible]);

  // const detailNodeList = (_isManage: boolean) => {
  //   return nodeList.filter((el) => _isManage || !el.disabled) || [];
  // };

  const sortableOptionsPage = {
    disabled: !isManage || nodeEdit,
    animation: 150,
    fallbackOnBody: true,
    swapThreshold: 0.65,
    ghostClass: 'qx-node-panel--drag',
    group: 'page',
  };

  const onExit = () => {
    setIsManage(false);
    setNodeList(cloneDeep(originalNodeList));
  };

  useEffect(() => {
    const _data = cloneDeep(originalNodeList);
    setNodeList(_data.filter((el) => isManage || !el.disabled) || []);
  }, [isManage]);
  const generateUniqueName = () => {
    if (nameList?.length) {
      let numberList: number[] = [];
      nameList.forEach((el) => {
        const a = /未命名分组\((.*?)\)/gi;
        const b = el.match(a);
        const c = el.split(b?.[0] || ''); // 判断前后是否还有字符
        const _firstStr = b?.[0].replace(a, '$1');
        if (!isNaN(Number(_firstStr)) && !c[1] && !c[0]) {
          numberList.push(Number(_firstStr));
        }
      });
      if (numberList.length) {
        numberList = numberList.sort();
        const _length = numberList.length;
        for (let i = 1; i <= numberList[_length - 1]; i++) {
          if (!numberList.includes(i)) {
            return `未命名分组(${i})`;
          }
        }
        return `未命名分组(${_length + 1})`;
      }
      return '未命名分组(1)';
    }
    return '未命名分组(1)';
  };

  // 新增分组
  const addGroup = () => {
    setNodeList((list) => {
      const _list = cloneDeep(list || []);
      _list.push({
        id: uuid(),
        name: generateUniqueName(),
        disabled: false,
        nodes: [],
        edit: true,
      });
      return _list;
    });
  };

  const saveData = () => {
    console.log(nodeList, 'llllllnodeList');
  };

  return (
    <Drawer
      visible={visible}
      onClose={onClose}
      width={475}
      className={`${prefix}`}
      title={null}
      closable={false}
    >
      <div className={`${prefix}__title`}>
        <div className={`${prefix}__title-left`}>节点类型</div>
        <div className={`${prefix}__title-right`}>
          {activeKey !== 'SYSTEM' && !isManage ? (
            <span
              className={`${prefix}--manage`}
              onClick={() => setIsManage(true)}
            >
              管理
            </span>
          ) : null}
          {isManage ? (
            <>
              <span onClick={() => onExit()}>退出</span>
              <span className={`${prefix}--save`} onClick={() => saveData()}>
                保存
              </span>
            </>
          ) : null}
        </div>
      </div>
      <Tabs
        activeKey={activeKey}
        className={`${prefix}__tabs`}
        onChange={(key) => setActiveKey(key)}
      >
        {panelTabs.map((item) => {
          return (
            <Tabs.TabPane tab={item.name} key={item.key}>
              {isManage && !loading ? (
                <div className={`${prefix}__opt`}>
                  <span
                    className={`${prefix}__opt-icon`}
                    onClick={() => addGroup()}
                  >
                    <QxIcon
                      className={`${prefix}__left-icon`}
                      type={'icon-frame-todolist'}
                    />
                    分组
                  </span>
                  {activeKey === 'DEVELOP' ? (
                    <span className={`${prefix}__opt-icon`} onClick={() => {}}>
                      <QxIcon
                        className={`${prefix}__left-icon`}
                        type={'icon-frame-todolist'}
                      />
                      开发节点管理
                    </span>
                  ) : null}
                </div>
              ) : null}
              <div
                className={`${prefix}__content ${
                  isManage ? `${prefix}__content--manage` : ''
                }`}
              >
                {loading ? (
                  <Spin className={`${prefix}__loading`} />
                ) : (
                  <>
                    {nodeList?.length ? (
                      <ReactSortable
                        list={nodeList}
                        setList={setNodeList}
                        {...sortableOptionsPage}
                      >
                        {nodeList.map((bl, index) => {
                          return (
                            <NodeList
                              key={bl.id}
                              listItem={bl}
                              isManage={isManage}
                              setNodeList={setNodeList}
                              nodeIndex={index}
                              setNodeEdit={setNodeEdit}
                            />
                          );
                        })}
                      </ReactSortable>
                    ) : (
                      <div className={`${prefix}--empty ${prefix}--empty-max`}>
                        <img src={Empty} alt={'暂无数据'} />
                        <span>暂无数据</span>
                      </div>
                    )}
                  </>
                )}
              </div>
            </Tabs.TabPane>
          );
        })}
      </Tabs>
    </Drawer>
  );
};

interface NodePanelProps extends INodePanelProps {}

export default memo(NodePanel);