import PropTypes from 'prop-types'; import { Input, Layout, Spin, Tree, ConfigProvider } from 'antd'; // import { SearchOutlined } from '@ant-design/icons'; import React, { useCallback, useEffect, useMemo, useRef, useState, useContext } from 'react'; import PandaEmpty from '@wisdom-components/empty'; import classNames from 'classnames'; import _ from 'lodash'; import { getEquipmentInfo } from './apis'; import './index.less'; import classnames from 'classnames'; // 生成树的数据格式 const treeDataGenerator = (origin, currentType, ref) => { if (!origin || !Array.isArray(origin)) return []; return origin.map((item) => { const { shortName, deviceName, code, deviceType, children: originChildren } = item; const title = shortName || deviceName; const children = treeDataGenerator(originChildren, currentType); const _width = (ref?.current?.getBoundingClientRect().width || 180) - 24 - 8 - 28; // 24为tree的左侧空白,8为右侧滚动条位置,28是左侧复选框和边距等 return { ...item, title: ( <div title={title} style={{ // width: _width, overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis', }} > {title} </div> ), key: code, children, disabled: currentType ? !(currentType === deviceType) : false, }; }); }; // 树结构展开平铺 const flattenTreeData = (treeData) => { if (!treeData || !Array.isArray(treeData)) return {}; const result = {}; const deep = (treeData, target) => { treeData && treeData.forEach((item) => { target[item.key] = item; if (item.children && item.children.length > 0) { deep(item.children, target); } }); }; deep(treeData, result); return result; }; // 图层树处理 const convertToTree = (data, name) => { let obj = {}; let newData = []; data.forEach((item, index) => { const state = item[name] || '未知'; if (obj[state]) { obj[state] = [...obj[state], item]; } else { obj[state] = [item]; } }); for (let k in obj) { newData.push({ title: k, children: [...obj[k]], key: k, selectable: false, disableCheckbox: true, checkable: false, }); expandedKeys.push(k); } return newData; }; let dataList = []; let expandedKeys = []; const DeviceTree = (props) => { const { deviceTypes = '二供泵房,二供机组', userAccessor = false, getChild = true, sortFields = '', direction = '', classField = '', customerName = '', } = props; const { getPrefixCls } = useContext(ConfigProvider.ConfigContext); const prefixCls = getPrefixCls('ec-device-tree-group'); const cusProps = _.pick(props, ['onSelect', 'onCheck', 'selectable', 'checkable']); const { onSelect, onCheck, keepChecked, singleType = false, selectable, checkable } = props; const [data, setData] = useState([]); const [{ pageIndex, pageSize }, setPagination] = useState({ pageIndex: 1, pageSize: props.pageSize || 500, }); const [searchs, setSearchs] = useState(''); const [loading, setLoading] = useState(true); const [hasMore, setHasMore] = useState(true); const ref = useRef(); const dataRef = useRef({ currentType: '', preCheckedData: [], preCheckedKeys: [], preSelectedData: [], flatData: {}, }); const { currentType, flatData } = dataRef.current; const [checkedKeys, setCheckedKeys] = useState([]); const [selectedKeys, setSelectedKeys] = useState([]); const [first, setFirst] = useState(true); const requestQuery = useMemo(() => { return { deviceTypes, pageIndex, pageSize, queryInfo: searchs, getChild, userID: userAccessor ? window?.globalConfig?.userInfo?.OID : void 0, userAccess: userAccessor, sortFields, classField, direction, customerName, }; }, [ customerName, deviceTypes, direction, getChild, pageIndex, pageSize, searchs, sortFields, classField, ]); useEffect(() => { setLoading(true); getEquipmentInfo(requestQuery) .then((res) => { setLoading(false); const { list, pageIndex, totalCount } = res.data || {}; const newData = pageIndex === 1 ? list : [...dataList, ...list]; // 非第一页时合并 // 是否需要默认选中 if (pageIndex === 1 && (!keepChecked || (keepChecked && !checkedKeys.length))) { setCheckedKeys(newData.length > 0 ? [newData[0].code] : []); onCheck?.(newData.length > 0 ? [newData[0]] : []); // 仅单选时使用select相关数据 !checkable && onSelect?.(newData.length > 0 ? [newData[0]] : []); !checkable && setSelectedKeys(newData.length > 0 ? [newData[0].code] : []); singleType && (dataRef.current.currentType = newData[0].deviceType); } setData(() => { dataList = [...newData]; return newData; }); props.setDeviceList(newData); props.setSearchStr(searchs); setHasMore(newData.length < totalCount); }) .catch((err) => { setLoading(false); }); }, [requestQuery]); const onScroll = useCallback(() => { if (loading || !hasMore) return; const { clientHeight, scrollHeight, scrollTop } = ref.current; if (clientHeight + scrollTop + 10 >= scrollHeight) { // 加载更多 setPagination({ pageSize, pageIndex: pageIndex + 1, }); } }, [loading, pageIndex, pageSize, hasMore]); const onSearch = useCallback( (value) => { setSearchs(value); setPagination({ pageIndex: 1, pageSize, }); }, [pageSize], ); useEffect(() => { ref.current?.addEventListener('scroll', onScroll, true); return () => { // eslint-disable-next-line react-hooks/exhaustive-deps ref.current?.removeEventListener('scroll', onScroll, true); }; }, [onScroll]); const treeData = useMemo(() => { const treeData = treeDataGenerator(data, singleType && currentType, ref); const flatData = flattenTreeData(treeData); dataRef.current.flatData = Object.assign(dataRef.current.flatData || {}, flatData); return convertToTree(treeData, requestQuery.classField ? 'deviceClass' : 'deviceType'); }, [data, singleType, currentType]); const handleCheck = (keys, info) => { // keys中会保留多余的key const { checked } = keys; setCheckedKeys(checked); const data = checked.map((key) => flatData[key]); onCheck?.(data); singleType && data.length === 0 && (dataRef.current.currentType = ''); singleType && data.length === 1 && (dataRef.current.currentType = data[0].deviceType); }; const handleSelect = (keys, info) => { if (checkable) { setCheckedKeys(keys); const data = keys.map((key) => flatData[key]); onCheck?.(data); singleType && data.length === 0 && (dataRef.current.currentType = ''); singleType && data.length === 1 && (dataRef.current.currentType = data[0].deviceType); } else { setSelectedKeys(keys); const data = keys.map((key) => flatData[key]); onSelect?.(data); } }; return ( <div className={classNames(prefixCls, 'wkt-scroll-light')}> <Input.Search placeholder="搜索设备名称" onSearch={onSearch} allowClear /> <div className={classnames(`${prefixCls}-tree-wrap`, 'wkt-scroll-light-plus')} ref={ref}> <Spin spinning={loading}> {data && data.length ? ( <Tree checkStrictly checkedKeys={checkedKeys} selectedKeys={selectedKeys} autoExpandParent treeData={treeData} defaultExpandParent defaultExpandedKeys={expandedKeys} {...cusProps} onCheck={handleCheck} onSelect={handleSelect} /> ) : ( <PandaEmpty /> )} </Spin> </div> </div> ); }; DeviceTree.defaultProps = { deviceTypes: '二供泵房', userAccessor: false, getChild: false, sortFields: '', direction: '', customerName: '', classField: '', selectable: false, checkable: false, keepChecked: false, onSelect: () => {}, onCheck: () => {}, setDeviceList: () => {}, setSearchStr: () => {}, }; DeviceTree.propTypes = { deviceTypes: PropTypes.string, userAccessor: PropTypes.bool, getChild: PropTypes.bool, sortFields: PropTypes.string, direction: PropTypes.string, customerName: PropTypes.string, classField: PropTypes.string, selectable: PropTypes.bool, checkable: PropTypes.bool, keepChecked: PropTypes.bool, onSelect: PropTypes.func, onCheck: PropTypes.func, setDeviceList: PropTypes.func, setSearchStr: PropTypes.func, }; export default DeviceTree;