diff --git a/src/table/BaseTable.tsx b/src/table/BaseTable.tsx index 54eea2c37..666b8e6f8 100644 --- a/src/table/BaseTable.tsx +++ b/src/table/BaseTable.tsx @@ -231,6 +231,21 @@ const BaseTable = forwardRef((props, ref) => { ellipsisOverlayClassName: props.size !== 'medium' ? sizeClassNames[props.size] : '', }; + const headUseMemoDependencies = [ + resizable, + thWidthList, + isFixedHeader, + rowAndColFixedPosition, + isMultipleHeader, + spansAndLeafNodes, + thList, + columnResizeParams, + classPrefix, + props.bordered, + props.resizable, + props.size, + ]; + // 多级表头左边线缺失 const affixedLeftBorder = props.bordered ? 1 : 0; @@ -388,20 +403,71 @@ const BaseTable = forwardRef((props, ref) => { {isVirtual &&
} {renderColGroup(false)} - {props.showHeader && } - - + {useMemo(() => { + if (!props.showHeader) return null; + return ; + // eslint-disable-next-line + }, headUseMemoDependencies)} + + {useMemo( + () => ( + + ), + // eslint-disable-next-line + [ + tableBodyProps.classPrefix, + tableBodyProps.ellipsisOverlayClassName, + tableBodyProps.rowAndColFixedPosition, + tableBodyProps.showColumnShadow, + tableBodyProps.data, + tableBodyProps.columns, + tableBodyProps.tableElm, + tableBodyProps.tableContentElm, + tableBodyProps.tableWidth, + isWidthOverflow, + props.rowKey, + props.rowClassName, + props.rowAttributes, + props.loading, + props.empty, + props.fixedRows, + props.firstFullRow, + props.lastFullRow, + props.rowspanAndColspan, + props.scroll, + props.cellEmptyContent, + ], + )} + + {useMemo( + () => ( + + ), + // eslint-disable-next-line + [ + isFixedHeader, + rowAndColFixedPosition, + spansAndLeafNodes, + columns, + thWidthList, + props.rowKey, + props.footData, + props.rowAttributes, + props.rowClassName, + props.footerSummary, + ], + )}
); @@ -431,7 +497,25 @@ const BaseTable = forwardRef((props, ref) => {
{!!topContent &&
{topContent}
} - {renderAffixedHeader()} + {useMemo( + () => renderAffixedHeader(), + // eslint-disable-next-line + [ + // eslint-disable-next-line + ...headUseMemoDependencies, + showAffixHeader, + tableWidth, + tableElmWidth, + affixHeaderRef, + affixedLeftBorder, + tableElmClasses, + tableElementStyles, + columns, + spansAndLeafNodes, + props.showHeader, + props.headerAffixedTop, + ], + )} {tableContent} diff --git a/src/table/Cell.tsx b/src/table/Cell.tsx new file mode 100644 index 000000000..08fdb88bf --- /dev/null +++ b/src/table/Cell.tsx @@ -0,0 +1,129 @@ +import React, { MouseEvent } from 'react'; +import classNames from 'classnames'; +import isFunction from 'lodash/isFunction'; +import get from 'lodash/get'; +import TEllipsis from './Ellipsis'; +import { BaseTableCellParams, RowspanColspan, TableRowData, TdBaseTableProps } from './type'; +import { RowAndColFixedPosition } from './interface'; +import { getColumnFixedStyles } from './hooks/useFixed'; +import { TableClassName } from './hooks/useClassName'; +import { formatClassNames } from './utils'; +import { TooltipProps } from '../tooltip'; + +export interface RenderEllipsisCellParams { + cellNode: any; + tableElm?: HTMLDivElement; + columnLength: number; + classPrefix?: string; + overlayClassName?: string; +} + +export interface CellProps { + cellParams: BaseTableCellParams; + rowAndColFixedPosition: RowAndColFixedPosition; + columnLength: number; + dataLength: number; + cellSpans: RowspanColspan; + cellEmptyContent: TdBaseTableProps['cellEmptyContent']; + tableClassNames: TableClassName; + tableElm?: HTMLDivElement; + classPrefix?: string; + overlayClassName?: string; + rowspanAndColspan: TdBaseTableProps['rowspanAndColspan']; + onClick?: (e: MouseEvent) => void; +} + +export function renderCell( + params: BaseTableCellParams, + extra?: { + cellEmptyContent?: TdBaseTableProps['cellEmptyContent']; + }, +) { + const { col, row, rowIndex } = params; + // support serial number column + if (col.colKey === 'serial-number') { + return rowIndex + 1; + } + if (isFunction(col.cell)) { + return col.cell(params); + } + if (isFunction(col.render)) { + return col.render({ ...params, type: 'cell' }); + } + const r = col.cell || col.render || get(row, col.colKey); + // 0 和 false 属于正常可用之,不能使用兜底逻辑 cellEmptyContent + if (![undefined, '', null].includes(r)) return r; + if (extra?.cellEmptyContent) return extra.cellEmptyContent; + return r; +} + +function renderEllipsisCell(cellParams: BaseTableCellParams, params: RenderEllipsisCellParams) { + const { cellNode, tableElm, columnLength, classPrefix, overlayClassName } = params; + const { col, colIndex } = cellParams; + let content = isFunction(col.ellipsis) ? col.ellipsis(cellParams) : undefined; + if (typeof col.ellipsis === 'object' && isFunction(col.ellipsis.content)) { + content = col.ellipsis.content(cellParams); + } + let tooltipProps = {}; + if (typeof col.ellipsis === 'object') { + tooltipProps = 'props' in col.ellipsis ? col.ellipsis.props : col.ellipsis || undefined; + } + const tableElement = tableElm; + let placement: TooltipProps['placement'] = colIndex === 0 ? 'top-left' : 'top'; + placement = colIndex === columnLength - 1 ? 'top-right' : placement; + return ( + tableElement : undefined} + popupContent={content} + tooltipProps={tooltipProps} + overlayClassName={overlayClassName} + classPrefix={classPrefix} + > + {cellNode} + + ); +} + +const Cell = (props: CellProps) => { + const { cellParams, tableClassNames, tableElm, columnLength, classPrefix, overlayClassName } = props; + const { col, colIndex, rowIndex } = cellParams; + const { cellSpans, dataLength, rowAndColFixedPosition, cellEmptyContent, rowspanAndColspan, onClick } = props; + const { tableColFixedClasses, tdEllipsisClass, tableBaseClass, tdAlignClasses, tableDraggableClasses } = + tableClassNames; + + const cellNode = renderCell(cellParams, { cellEmptyContent }); + const tdStyles = getColumnFixedStyles(col, colIndex, rowAndColFixedPosition, tableColFixedClasses); + const customClasses = formatClassNames(col.className, { ...cellParams, type: 'td' }); + const classes = [ + tdStyles.classes, + customClasses, + { + [tdEllipsisClass]: col.ellipsis, + [tableBaseClass.tdLastRow]: rowIndex + cellSpans.rowspan === dataLength, + [tableBaseClass.tdFirstCol]: colIndex === 0 && rowspanAndColspan, + [tdAlignClasses[col.align]]: col.align && col.align !== 'left', + // 标记可拖拽列 + [tableDraggableClasses.handle]: col.colKey === 'drag', + }, + ]; + const normalAttrs = isFunction(col.attrs) ? col.attrs({ ...cellParams, type: 'td' }) : col.attrs; + const attrs = { ...normalAttrs, rowSpan: cellSpans.rowspan, colSpan: cellSpans.colspan }; + return ( + + {col.ellipsis + ? renderEllipsisCell(cellParams, { cellNode, tableElm, columnLength, classPrefix, overlayClassName }) + : cellNode} + + ); +}; + +Cell.displayName = 'Cell'; + +export default Cell; diff --git a/src/table/EditableCell.tsx b/src/table/EditableCell.tsx index a95b8aaeb..c3cd74980 100644 --- a/src/table/EditableCell.tsx +++ b/src/table/EditableCell.tsx @@ -14,7 +14,7 @@ import { } from './type'; import useGlobalIcon from '../hooks/useGlobalIcon'; import { TableClassName } from './hooks/useClassName'; -import { renderCell } from './TR'; +import { renderCell } from './Cell'; import { validate } from '../form/formModel'; import log from '../_common/js/log'; import { AllValidateResult } from '../form/type'; diff --git a/src/table/TR.tsx b/src/table/TR.tsx index 12fb84b7f..8c2f467b5 100644 --- a/src/table/TR.tsx +++ b/src/table/TR.tsx @@ -1,28 +1,14 @@ import React, { useMemo, useRef, MouseEvent } from 'react'; -import isFunction from 'lodash/isFunction'; import get from 'lodash/get'; import classnames from 'classnames'; -import { formatClassNames, formatRowAttributes, formatRowClassNames } from './utils'; -import { getRowFixedStyles, getColumnFixedStyles } from './hooks/useFixed'; +import { formatRowAttributes, formatRowClassNames } from './utils'; +import { getRowFixedStyles } from './hooks/useFixed'; import { RowAndColFixedPosition } from './interface'; import useClassName from './hooks/useClassName'; -import TEllipsis from './Ellipsis'; -import { BaseTableCellParams, TableRowData, RowspanColspan, TdBaseTableProps, TableScroll } from './type'; +import { TableRowData, RowspanColspan, TdBaseTableProps, TableScroll } from './type'; import useLazyLoad from './hooks/useLazyLoad'; import { getCellKey, SkipSpansValue } from './hooks/useRowspanAndColspan'; -import { TooltipProps } from '../tooltip'; - -export interface RenderTdExtra { - rowAndColFixedPosition: RowAndColFixedPosition; - columnLength: number; - dataLength: number; - cellSpans: RowspanColspan; - cellEmptyContent: TdBaseTableProps['cellEmptyContent']; -} - -export interface RenderEllipsisCellParams { - cellNode: any; -} +import Cell from './Cell'; export type TrCommonProps = Pick; @@ -70,30 +56,6 @@ export interface TrProps extends TrCommonProps { export const ROW_LISTENERS = ['click', 'dblclick', 'mouseover', 'mousedown', 'mouseenter', 'mouseleave', 'mouseup']; -export function renderCell( - params: BaseTableCellParams, - extra?: { - cellEmptyContent?: TdBaseTableProps['cellEmptyContent']; - }, -) { - const { col, row, rowIndex } = params; - // support serial number column - if (col.colKey === 'serial-number') { - return rowIndex + 1; - } - if (isFunction(col.cell)) { - return col.cell(params); - } - if (isFunction(col.render)) { - return col.render({ ...params, type: 'cell' }); - } - const r = col.cell || col.render || get(row, col.colKey); - // 0 和 false 属于正常可用之,不能使用兜底逻辑 cellEmptyContent - if (![undefined, '', null].includes(r)) return r; - if (extra?.cellEmptyContent) return extra.cellEmptyContent; - return r; -} - // 表格行组件 export default function TR(props: TrProps) { const { @@ -111,14 +73,7 @@ export default function TR(props: TrProps) { const trRef = useRef(); - const { - tdEllipsisClass, - tableBaseClass, - tableColFixedClasses, - tableRowFixedClasses, - tdAlignClasses, - tableDraggableClasses, - } = useClassName(); + const classNames = useClassName(); const trStyles = getRowFixedStyles( get(row, rowKey || 'id'), @@ -126,7 +81,7 @@ export default function TR(props: TrProps) { dataLength, fixedRows, rowAndColFixedPosition, - tableRowFixedClasses, + classNames.tableRowFixedClasses, ); const trAttributes = useMemo( @@ -142,71 +97,6 @@ export default function TR(props: TrProps) { const useLazyLoadParams = useMemo(() => ({ ...scroll, rowIndex }), [scroll, rowIndex]); const { hasLazyLoadHolder, tRowHeight } = useLazyLoad(tableContentElm, trRef.current, useLazyLoadParams); - function renderEllipsisCell(cellParams: BaseTableCellParams, params: RenderEllipsisCellParams) { - const { cellNode } = params; - const { col, colIndex } = cellParams; - let content = isFunction(col.ellipsis) ? col.ellipsis(cellParams) : undefined; - if (typeof col.ellipsis === 'object' && isFunction(col.ellipsis.content)) { - content = col.ellipsis.content(cellParams); - } - let tooltipProps = {}; - if (typeof col.ellipsis === 'object') { - tooltipProps = 'props' in col.ellipsis ? col.ellipsis.props : col.ellipsis || undefined; - } - const tableElement = props.tableElm; - let placement: TooltipProps['placement'] = colIndex === 0 ? 'top-left' : 'top'; - placement = colIndex === props.columns.length - 1 ? 'top-right' : placement; - return ( - tableElement : undefined} - popupContent={content} - tooltipProps={tooltipProps} - overlayClassName={props.ellipsisOverlayClassName} - classPrefix={props.classPrefix} - > - {cellNode} - - ); - } - - function renderTd(params: BaseTableCellParams, extra: RenderTdExtra) { - const { col, colIndex, rowIndex } = params; - const { cellSpans, dataLength, rowAndColFixedPosition } = extra; - const cellNode = renderCell(params, { cellEmptyContent: props.cellEmptyContent }); - const tdStyles = getColumnFixedStyles(col, colIndex, rowAndColFixedPosition, tableColFixedClasses); - const customClasses = formatClassNames(col.className, { ...params, type: 'td' }); - const classes = [ - tdStyles.classes, - customClasses, - { - [tdEllipsisClass]: col.ellipsis, - [tableBaseClass.tdLastRow]: rowIndex + cellSpans.rowspan === dataLength, - [tableBaseClass.tdFirstCol]: colIndex === 0 && props.rowspanAndColspan, - [tdAlignClasses[col.align]]: col.align && col.align !== 'left', - // 标记可拖拽列 - [tableDraggableClasses.handle]: col.colKey === 'drag', - }, - ]; - const onClick = (e: MouseEvent) => { - const p = { ...params, e }; - props.onCellClick?.(p); - }; - const normalAttrs = isFunction(col.attrs) ? col.attrs({ ...params, type: 'td' }) : col.attrs; - const attrs = { ...normalAttrs, rowSpan: cellSpans.rowspan, colSpan: cellSpans.colspan }; - return ( - - {col.ellipsis ? renderEllipsisCell(params, { cellNode }) : cellNode} - - ); - } - // const { row, rowIndex, dataLength, rowAndColFixedPosition, scrollType, isInit } = props; // const hasHolder = scrollType === 'lazy' && !isInit; // const rowHeightRef: Ref = inject('rowHeightRef'); @@ -226,13 +116,27 @@ export default function TR(props: TrProps) { spanState?.colspan > 1 && (cellSpans.colspan = spanState.colspan); if (spanState.skipped) return null; } - return renderTd(params, { - dataLength, - rowAndColFixedPosition, - columnLength: props.columns.length, - cellSpans, - cellEmptyContent: props.cellEmptyContent, - }); + const onClick = (e: MouseEvent) => { + const p = { ...params, e }; + props.onCellClick?.(p); + }; + return ( + + ); }); const rowParams = { row, index: rowIndex }; diff --git a/src/table/hooks/useTreeData.tsx b/src/table/hooks/useTreeData.tsx index 1f29a14a6..d66852683 100644 --- a/src/table/hooks/useTreeData.tsx +++ b/src/table/hooks/useTreeData.tsx @@ -9,7 +9,7 @@ import classNames from 'classnames'; import TableTreeStore, { SwapParams } from '../../_common/js/table/tree-store'; import { TdEnhancedTableProps, PrimaryTableCol, TableRowData, TableRowValue, TableRowState } from '../type'; import useClassName from './useClassName'; -import { renderCell } from '../TR'; +import { renderCell } from '../Cell'; import { useLocaleReceiver } from '../../locale/LocalReceiver'; import useGlobalIcon from '../../hooks/useGlobalIcon';