import PropTypes from 'prop-types'; import { Input, Layout, Spin, Tree, ConfigProvider, Skeleton, Select } 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, GetFieldValueFromTable } from './apis'; import './index.less'; // 生成树的数据格式 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 deviceArr = deviceTypes ? deviceTypes.split(',') : []; 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, filterName, filterValue, } = 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 [filterFieldEntries, setFilterFieldEntries] = useState([]); // "筛选字段"可选列表 const [filterFieldValue, setFilterFieldValue] = useState(filterValue || ''); const ref = useRef(); const dataRef = useRef({ currentType: '', preCheckedData: [], preCheckedKeys: [], preSelectedData: [], flatData: {}, }); const { currentType, flatData } = dataRef.current; const [checkedKeys, setCheckedKeys] = useState([]); const [selectedKeys, setSelectedKeys] = useState([]); useEffect(() => { if (filterName && deviceArr.length === 1) { let device = deviceTypes.split(',')[0]; GetFieldValueFromTable({ tableName: '设备_台账_' + device, fieldName: filterName, }) .then((res) => { if (res.code == '0') { setFilterFieldEntries(res.data); } }) .catch((err) => { setFilterFieldEntries([]); }); } }, [filterName]); const requestQuery = useMemo(() => { const qurey = { deviceTypes, pageIndex, pageSize, queryInfo: searchs, getChild, userID: userAccessor ? window?.globalConfig?.userInfo?.OID : void 0, userAccess: userAccessor, sortFields, classField, direction, customerName, }; if (deviceArr.length === 1) { qurey.queryWhereParams = [ { column: filterName, isString: true, matchingRule: '等于', columnValue: filterFieldValue, }, ]; } return qurey; }, [ customerName, deviceTypes, direction, getChild, pageIndex, pageSize, searchs, sortFields, classField, filterFieldValue, ]); 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 && !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 onChangeFilter = (val) => { setFilterFieldValue(val ? val : ''); }; 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')}> {filterName && deviceArr.length === 1 && !filterValue ? ( <div style={{ display: 'flex', alignItems: 'center', marginBottom: '10px' }}> <div style={{ flex: 'none' }}>{filterName}:</div> <Select style={{ flex: 1 }} placeholder="" onChange={onChangeFilter} allowClear={true}> {filterFieldEntries.map((item) => ( <Select.Option key={item}>{item}</Select.Option> ))} </Select> </div> ) : ( '' )} <Input.Search placeholder="搜索设备名称" onSearch={onSearch} allowClear /> <div className={classNames(`${prefixCls}-tree-wrap`, 'wkt-scroll-light-plus')} style={{ height: filterName ? 'calc(100% - 90px)' : 'calc(100% - 48px)' }} ref={ref} > {/* <Spin spinning={loading}> */} {loading || (data && data.length) ? ( <Skeleton loading={loading} active> <Tree checkStrictly checkedKeys={checkedKeys} selectedKeys={selectedKeys} autoExpandParent treeData={treeData} defaultExpandParent defaultExpandedKeys={expandedKeys} {...cusProps} onCheck={handleCheck} onSelect={handleSelect} /> </Skeleton> ) : ( <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, filterName: PropTypes.string, // 筛选字段,只有一个设备类型时有效 filterValue: PropTypes.string, // 筛选字段默认值 ,有值时,不显示下拉 }; export default DeviceTree;