rel-setter.tsx 10 KB
import React, { useEffect, useState } from 'react';
import { Empty, Input, List, Modal, Tag } from 'antd';
import { CheckOutlined, CloseOutlined } from '@ant-design/icons';
import { getPreviewSelect, getSelect } from '../service';
import VirtualList from 'rc-virtual-list';
import { ParamValueType } from '../../qx-filter-condition/filter';

interface UserSelectProps {
  value?: string[];
  onChange: (val: any, isDelete?: boolean) => void;
  disabled: boolean;
  params: any;
  isMultiple?: boolean; //表示单多选
  getName: (str: string[]) => string;
  modalClassName?: string | undefined; // 弹框类名自定义 用于自定义以及覆盖样式
}
import {QxBaseIcon} from '@qx/common';

const CONTAINER_HEIGHT = 300;

/**
 * 用户选择
 *
 * @param value
 * @param onChange
 * @param disabled
 * @constructor
 */
export const RelSetter: React.FC<UserSelectProps> = ({
  value,
  onChange,
  disabled = false,
  isMultiple = true,
  ...props
}) => {
  const [hasNextPage, setHasNextPage] = useState<boolean>(true);
  const [params, setParams] = useState<any>({ pageSize: 10, pageNum: 1, keyword: '' });

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

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

  const getData = (_params?: any) => {
    if (loading) {
      return;
    }
    const params1 = { ...params, ...(_params || {}) };

    if (!hasNextPage && params1.pageNum !== 1) {
      return;
    }
    setLoading(true);
    let request = getPreviewSelect;
    const _data = { field: props.params.field, ...(params1 || {}) };
    const __params = {};
    let funCode = props.params.funCode;

    if (props.params.useId) {
      request = getSelect;
      delete _data.field;
      // @ts-ignore
      __params.withId = true;
      funCode = props.params.relId;
    }
    request(props.params.appCode ? props.params.appCode : 'id', funCode, _data, __params)
      .then((res) => {
        setHasNextPage(res.hasNextPage);
        setParams({ ...params1, pageNum: params1.pageNum + 1 });
        let _list = res.list || [];
        if (params1.pageNum > 1) {
          _list = list.concat(_list);
        }
        setList(_list);
        setLoading(false);
      })
      .catch(() => {
        setLoading(false);
      });
  };

  useEffect(() => {
    const _valueMap = {};
    const manualValues: string[] = [];
    const waitCodes: string[] = [];
    const fieldValues: string[] = [];
    (value || []).forEach((item) => {
      // @ts-ignore
      if (item.type === ParamValueType.MANUAL) {
        // @ts-ignore
        manualValues.push(item.value);
        // @ts-ignore
      } else if (item.type === ParamValueType.FIELD) {
        // @ts-ignore
        fieldValues.push(item.value);
      }
    });
    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(',');
      });
    }
    if (waitCodes.length > 0) {
      const _data = { field: props.params.field, code: waitCodes.join(',') };
      const __params = {};
      let funCode = props.params.funCode;
      let request = getPreviewSelect;
      if (props.params.useId) {
        request = getSelect;
        delete _data.field;
        // @ts-ignore
        __params.withId = true;
        funCode = props.params.relId;
      }
      request(props.params.appCode ? props.params.appCode : 'id', funCode, _data, __params)
        .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 => item.value);
    const _valueMap = { ...valueMap };
    // console.log(tmpSelectedKeys,values,_valueMap,tmpSelected,list)
    // tmpSelectedKeys.forEach((key, index) => {
    //   if (values.indexOf(key) === -1) {
    //     values.push(key);
    //     _valueMap[key] = tmpSelected[index].name;
    //   }
    // })

    // onChange(values);
    onChange(tmpSelectedKeys);
    //将新增的选项加进valueMap中    对于去除的选项   不做处理
    tmpSelectedKeys.forEach((item: any) => {
      if (!_valueMap[item]) {
        // @ts-ignore
        _valueMap[item] = list.filter((o: any) => o.code === item)[0]?.name;
      }
    });

    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 onScroll = (e: React.UIEvent<HTMLElement, UIEvent>) => {
    // @ts-ignore
    if (e.target.scrollHeight - e.target.scrollTop === CONTAINER_HEIGHT) {
      getData();
    }
  };

  const handleChange = (e: { type: string; target: { value: string } }) => {
    if (e.type === 'click' && e.target.value === '' && params.keyword !== '') {
      //清空
      getData({ ...params, pageNum: 1, keyword: '' });
    }
  };
  const handleSearch = (e: React.KeyboardEvent<HTMLInputElement>) => {
    // @ts-ignore
    const keyword = e.target.value;
    getData({ ...params, pageNum: 1, keyword });
  };

  const handleSelect = (item: { code: any }) => {
    // console.log(item)
    let _tmpSelectedKeys = [];
    let _tmpSelected = [];
    if (isMultiple) {
      _tmpSelectedKeys = [...(tmpSelectedKeys || [])];
      _tmpSelected = [...(tmpSelected || [])];
      let isChecked = true;
      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);
      }
    } else {
      _tmpSelectedKeys.push(item.code);
      _tmpSelected.push(item);
    }
    setTmpSelectedKey(_tmpSelectedKeys);
    setTmpSelected(_tmpSelected);
  };

  return (
    <>
      <div
        className={`select sel-user-show ant-input btn-text ${
          disabled ? 'ant-input-disabled' : ''
        }`}
        onClick={() => {
          if (disabled) {
            return;
          }
          setVisible(true);
          // console.log(tmpSelectedKeys,value)
          // @ts-ignore
          setTmpSelectedKey(
            (value || []).map((o: any) => {
              return o.value;
            }),
          );
          getData({ pageNum: 1, keyword: '' });
        }}
      >
        {(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>
          <div className={'qx-search-menus__wrap'} style={{ borderBottom: '1px solid #f0f0f0' }}>
            <Input
              className={'qx-selector-sub-search'}
              placeholder={'输入关键字,回车搜索'}
              allowClear
              prefix={<QxBaseIcon type={'icon-app-search-line'} />}
              onChange={handleChange}
              onPressEnter={(e) => {
                handleSearch(e);
              }}
            />
          </div>
          {!loading && list.length === 0 ? (
            <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
          ) : (
            <List
              loading={loading}
              className={'qg-rel-select--list'}
              style={{
                maxHeight: CONTAINER_HEIGHT + 'px',
                position: 'relative',
                overflow: 'hidden',
              }}
            >
              <VirtualList
                data={list}
                height={CONTAINER_HEIGHT}
                itemHeight={47}
                itemKey="code"
                onScroll={(e) => onScroll(e)}
              >
                {(item) => (
                  <List.Item
                    // @ts-ignore
                    key={item.code}
                    className={
                      // @ts-ignore
                      (tmpSelectedKeys || []).indexOf(item.code) > -1
                        ? 'qg-rel-list-item--selected'
                        : ''
                    }
                    onClick={() => handleSelect(item)}
                  >
                    {/*@ts-ignore*/}
                    <List.Item.Meta title={item.name} />
                    <div>
                      {/*// @ts-ignore*/}
                      {(tmpSelectedKeys || []).indexOf(item.code) > -1 ? <CheckOutlined /> : null}
                    </div>
                  </List.Item>
                )}
              </VirtualList>
            </List>
          )}
        </div>
      </Modal>
    </>
  );
};