rel-tree-setter.tsx 9.71 KB
import React, { useCallback, useEffect, useState } from 'react';
import { Empty, Input, Modal, Spin, Tag, Tree } from 'antd';
import { CloseOutlined } from '@ant-design/icons';
import { getPreviewSelect, getSelect } from '../service';
import { ParamValueType } from '../../qx-filter-condition/filter';
import {QxBaseIcon} from '@qx/common';

interface UserSelectProps {
  value?: string[];
  onChange: (val: any, isDelete?: boolean) => void;
  disabled: boolean;
  params: any;
  isMultiple?: boolean;
  getName: (str: string[]) => string;
  inputDis?: boolean; // 是否触发弹窗
  modalClassName?: string | undefined; // 弹框类名自定义 用于自定义以及覆盖样式
}

// const CONTAINER_HEIGHT = 300;

/**
 * 用户选择
 *
 * @param value
 * @param onChange
 * @param disabled
 * @constructor
 */
export const RelTreeSetter: React.FC<UserSelectProps> = ({
  value,
  onChange,
  disabled = false,
  isMultiple,
  inputDis,
  ...props
}) => {
  const [loading, setLoading] = useState<boolean>(false);
  // const [data, setData] = useState<any>([])
  const [treeData, setTreeData] = useState<any>([]);
  const [initData, setInitData] = useState<any>([]);
  // const [expandedKeys, setExpandedKeys] = useState<any>([])

  const [valueMap, setValueMap] = useState({});
  const [tmpSelected, setTmpSelected] = useState<any[]>([]);
  const [tmpSelectedKeys, setTmpSelectedKey] = useState<string[]>([]);
  // const [list, setList] = useState<[]>([]);

  const [visible, setVisible] = useState(false);

  const getData = () => {
    if (!props.params) {
      // setData([]);
      setTreeData([]);
      return;
    }
    if (loading) {
      return;
    }

    setLoading(true);
    let request = getPreviewSelect;

    if (props.params.useId) {
      request = getSelect;
    }
    request(props.params.appCode ? props.params.appCode : 'id', props.params.funCode, {
      field: props.params.field,
    })
      .then((res) => {
        // setData(res);
        setTreeData(res);
        setInitData(res);
        setLoading(false);
      })
      .catch(() => {
        setLoading(false);
      });
  };

  useEffect(() => {
    if (visible) {
      getData();
    }
  }, [JSON.stringify(props.params), visible]);

  useEffect(() => {
    const _valueMap = {};
    const manualValues: string[] = [];
    const waitCodes: string[] = [];
    const fieldValues: string[] = [];
    (value || []).forEach((item: any) => {
      if (item.type === ParamValueType.MANUAL) {
        manualValues.push(item.value);
      } else if (item.type === ParamValueType.FIELD) {
        fieldValues.push(item.value);
      }
    });
    setTmpSelectedKey(manualValues || []);
    manualValues.forEach((item) => {
      if (valueMap[item]) {
        _valueMap[item] = valueMap[item];
      } else {
        waitCodes.push(item);
      }
    });

    //field 目前只能选一个
    if (typeof props.getName === 'function' && fieldValues.length > 0) {
      fieldValues.map((_val, index) => {
        _valueMap[fieldValues[index]] =
          props.getName([fieldValues[index]]) || fieldValues.join(',');
      });
      // _valueMap[fieldValues.join(',')] = props.getName(fieldValues) || fieldValues.join(',');
    }
    if (waitCodes.length > 0) {
      let request = getPreviewSelect;
      if (props.params.useId) {
        request = getSelect;
      }
      request(props.params.appCode ? props.params.appCode : 'id', props.params.funCode, {
        field: props.params.field,
        code: waitCodes.join(','),
      })
        .then((res) => {
          res.forEach((item) => {
            _valueMap[item.code] = item.name;
          });
          setValueMap(_valueMap);
        })
        .catch(() => {
          waitCodes.forEach((item) => {
            _valueMap[item] = item;
          });
          setValueMap(_valueMap);
        });
    } else {
      if (JSON.stringify(_valueMap) !== JSON.stringify(valueMap)) {
        setValueMap(_valueMap);
      }
    }
  }, [value]);

  /**
   * 选择完成
   */
  const handleOk = () => {
    const values: string[] = (value || []).map((item: any) => item.value);
    const _valueMap = { ...valueMap };
    tmpSelectedKeys.forEach((key, index) => {
      if (values.indexOf(key) === -1) {
        values.push(key);
        const item = (tmpSelected || []).filter((el) => el.code === key);
        _valueMap[key] = item?.[0]?.name;
      }
    });

    onChange(values);

    setValueMap(_valueMap);
    setTmpSelectedKey([]);
    setTmpSelected([]);
    setVisible(false);
  };

  const handleCancel = () => {
    setTmpSelectedKey([]);
    setTmpSelected([]);
    setVisible(false);
  };

  const removeItem = (e: any, item: string) => {
    e.preventDefault();
    const _valueMap = { ...valueMap };
    delete _valueMap[item];
    setValueMap(_valueMap);
    onChange(Object.keys(_valueMap), true);
  };

  const handleSelect = (checkedData, e) => {
    console.log(e.node, e.checked);
    const item = { code: e.node.code, name: e.node.name };
    let isChecked = true;
    let _tmpSelectedKeys = [...(tmpSelectedKeys || [])];
    let _tmpSelected = [...(tmpSelected || [])];
    const index = (tmpSelectedKeys || []).indexOf(item.code);
    if (index > -1) {
      isChecked = false;
    }
    if (isChecked) {
      _tmpSelectedKeys.push(item.code);
      _tmpSelected.push(item);
    } else {
      _tmpSelectedKeys.splice(index, 1);
      _tmpSelected.splice(index, 1);
    }

    // 树支持单选
    if (!isMultiple) {
      if (isChecked) {
        _tmpSelectedKeys = [item.code];
        _tmpSelected = [item];
      } else {
        _tmpSelectedKeys = [];
        _tmpSelected = [];
      }
    }
    console.log(_tmpSelectedKeys, '_tmpSelectedKeys');
    setTmpSelectedKey(_tmpSelectedKeys);
    setTmpSelected(_tmpSelected);
  };

  // 树形搜索
  const generateTreeData = useCallback((_data: any[], _keywords?: string): any[] => {
    const _treeNode: any[] = [];
    _data.map((item) => {
      if (typeof item.visible === 'boolean' && !item.visible) {
        return;
      }
      const _item: any = {
        ...item,
        children: [],
      };
      if (item.children) {
        _item.children = generateTreeData(item.children, _keywords);
      }
      _treeNode.push(_item);
    });
    return _treeNode;
  }, []);

  const filter = (word: string) => {
    const traverse = function (node: any) {
      const childNodes = node.children || [];
      node.visible = node.name.indexOf(word) > -1;
      childNodes.forEach((child: any) => {
        child.visible = child.name.indexOf(word) > -1;
        traverse(child);
      });

      if (!node.visible && childNodes.length) {
        node.visible = childNodes.some((child: any) => child.visible);
      }
    };
    if (initData) {
      const _data = JSON.parse(JSON.stringify(initData));
      if (word != '') {
        _data.forEach((item) => {
          traverse(item);
        });
      }
      setTreeData(generateTreeData(_data, word));
    }
  };

  // 搜索事件
  const handleChange = (e: { target: { value: string } }) => {
    const _keyWord = e.target.value;
    if (!!_keyWord) {
      filter(_keyWord);
    } else {
      setTreeData(initData || []);
    }
  };

  return (
    <>
      <div
        className={`select sel-user-show ant-input btn-text ${
          disabled ? 'ant-input-disabled' : ''
        }`}
        onClick={() => {
          if (disabled) {
            return;
          }
          if (!inputDis) {
            setVisible(true);
          }
        }}
      >
        {(value || []).length === 0 ? (
          <span className={'qx-filter__setter--selected'}>请选择</span>
        ) : null}
        {(value || []).map((item: any) => (
          <Tag
            key={`${item.value}`}
            closeIcon={<CloseOutlined />}
            closable
            onClose={(e) => removeItem(e, item.value)}
            onClick={(e) => e.stopPropagation()}
            bordered={false}
          >
            {valueMap[item.value]}
          </Tag>
        ))}
      </div>
      <Modal
        title="选择"
        width={'640px'}
        className={'qx-table-selector'}
        open={visible}
        destroyOnClose
        onOk={handleOk}
        onCancel={handleCancel}
        wrapClassName={props?.modalClassName || ''}
      >
        <div className={'qx-search-tree__wrap'}>
          <Input
            className={'qx-selector-sub-search'}
            style={{ borderBottom: '1px solid #f0f0f0' }}
            placeholder={'请输入关键字,按回车键搜索'}
            allowClear
            prefix={<QxBaseIcon type={'icon-app-search-line'} />}
            onChange={(e) => {
              handleChange(e);
            }}
            onPressEnter={(e: any) => {
              handleChange(e);
            }}
          />
          <div
            className={`qx-search-tree`}
            style={{ height: '300px', overflow: 'auto', paddingTop: '5px' }}
          >
            {!loading ? (
              treeData?.length > 0 ? (
                <Tree
                  blockNode
                  checkable={true}
                  fieldNames={{
                    title: 'name',
                    key: 'code',
                    children: 'children',
                  }}
                  defaultExpandAll={true}
                  checkStrictly={true}
                  treeData={treeData}
                  onCheck={handleSelect}
                  checkedKeys={tmpSelectedKeys || []}
                  selectable={false}
                />
              ) : (
                <Empty style={{ paddingTop: '30px' }} />
              )
            ) : null}
          </div>
          <Spin
            style={{ width: '100%', marginTop: '40px' }}
            spinning={loading}
            // indicator={<LoadingOutlined style={{ fontSize: 24, marginTop: '40px' }} spin />}
          />
        </div>
      </Modal>
    </>
  );
};