import memoize from 'memoize-one'; import { isEqual } from 'lodash'; import { CellLocation, DragDirection, EditingData, IndexColumn } from '@/lib/types'; import { GridBodyCell } from '@/lib/components/body/cell'; import { GroupPin, GroupSelection } from '@/lib/types/group'; import { CreateElement } from 'vue'; import { fixStyle } from '@/lib/utils/dom'; import { BODY_ROW } from '@/lib/constants/dom-role'; import { isBetween } from '@/lib/utils/math'; import { CELL_CALCULATING, CELL_CALCULATING_BOTTOM, CELL_CALCULATING_LEFT, CELL_CALCULATING_RIGHT, CELL_CALCULATING_TOP, CELL_SELECTED, CELL_SELECTED_BOTTOM, CELL_SELECTED_LEFT, CELL_SELECTED_RIGHT, CELL_SELECTED_TOP, } from '@/lib/constants/dom-class'; import { ORDER_COLUMN_KEY } from '@/lib/constants/filler'; import { OrderCell } from '@/lib/components/body/cell/order-cell'; import {log} from "@/lib/utils/log"; /** * 索引为 rowId */ const getAreaBoundaryInfo = (pin: IndexColumn['pin'], area: GroupSelection) => (columnIndex: number, rowIndex: number) => ( activeClass: string, topClass: string, bottomClass: string, leftClass: string, rightClass: string, ) => { if (!area || !area.hit) return null; const betweenColumn = isBetween(area.column); const betweenRow = isBetween(area.row); const isLeftLink = () => { return ( (pin === GroupPin.scroll && ['left', 'all'].includes(area.link)) || (pin === GroupPin.right && 'scroll' === area.link) ); }; const isRightLink = () => (pin === GroupPin.scroll && ['right', 'all'].includes(area.link)) || (pin === GroupPin.left && 'scroll' === area.link); return { [activeClass]: betweenColumn(columnIndex) && betweenRow(rowIndex), [topClass]: area.row[0] === rowIndex, [bottomClass]: area.row[1] === rowIndex, [leftClass]: !isLeftLink() && area.column[0] === columnIndex, [rightClass]: !isRightLink() && area.column[1] === columnIndex, }; }; const getBanDragClass = (direction: DragDirection) => { if (direction === DragDirection.top) { return { [CELL_CALCULATING_BOTTOM]: false }; } if (direction === DragDirection.bottom) { return { [CELL_CALCULATING_TOP]: false }; } if (direction === DragDirection.left) { return { [CELL_CALCULATING_RIGHT]: false }; } return { [CELL_CALCULATING_LEFT]: false }; }; // TODO 优化 const getClass = memoize( ( pin: IndexColumn['pin'], columnIndex: number, rowIndex: number, selectionArea: GroupSelection, calculatingArea: GroupSelection, calculatingDirection: DragDirection, ) => { return { ...getAreaBoundaryInfo(pin, calculatingArea)(columnIndex, rowIndex)( CELL_CALCULATING, CELL_CALCULATING_TOP, CELL_CALCULATING_BOTTOM, CELL_CALCULATING_LEFT, CELL_CALCULATING_RIGHT, ), ...getAreaBoundaryInfo(pin, selectionArea)(columnIndex, rowIndex)( CELL_SELECTED, CELL_SELECTED_TOP, CELL_SELECTED_BOTTOM, CELL_SELECTED_LEFT, CELL_SELECTED_RIGHT, ), ...getBanDragClass(calculatingDirection), }; }, isEqual, ); const whetherShowHandle = ( editor: IndexColumn['editor'], isActivated: boolean, isEdit: boolean, isCrossGroup: boolean, clazz: any, ) => { const selectionShow = clazz && clazz[CELL_SELECTED_RIGHT] && clazz[CELL_SELECTED_BOTTOM]; return !isCrossGroup && editor && !isEdit && (selectionShow || isActivated); }; /** * 创建 render 的渲染函数 * @param h * @param _useCache */ export const createBodyRowRender = (h: CreateElement, _useCache: boolean) => ( id: string, // 拖拽修改时变化 index: number, // column resize 变化 width: number, // 滚动时变化 top: number, // 选择区域时变化 selectionArea: GroupSelection, // 拖动计算时变化 calculatingArea: GroupSelection, // 拖动计算时变化 calculatingDirection: DragDirection, ) => ( columns: IndexColumn[], pin: IndexColumn['pin'], height: number, dbClickedBodyCell: CellLocation | null, clickedBodyCell: CellLocation | null, rowData: Record, ) => { // 初始化样式计算器 const cellRender = (column: IndexColumn) => { const editor = column.editor; const isEdit = dbClickedBodyCell?.columnId === column.id && dbClickedBodyCell?.rowId === id; const isActivated = clickedBodyCell?.columnId === column.id && clickedBodyCell?.rowId === id; const clazzStart = performance.now(); const clazz = getClass( pin, column.index, index, selectionArea, calculatingArea, calculatingDirection, ); log('[cost][clazz]', performance.now() - clazzStart); const isCrossGroup = !!selectionArea.link; const isShowHandle = whetherShowHandle( editor, isActivated, isEdit, isCrossGroup, clazz, ); const key = `${pin}_${id}_${column.id}`; return h(GridBodyCell, { key, props: { pin, rowIndex: index, columnIndex: column.index, rowId: id, columnId: column.id, height, left: column.left, width: column.width, editor, isEdit, isActivated, isShowHandle, data: rowData[column.id], calculator: column.calculator, selectionClass: clazz, }, }); }; const orderCellRender = (column: IndexColumn) => { return h(OrderCell, { key: `${id}_${column.id}`, props: { index, left: column.left, width: column.width, height, }, }); }; const cellsCacheStart = performance.now(); const rowCells = columns.map((column: IndexColumn) => column.field === ORDER_COLUMN_KEY ? orderCellRender(column) : cellRender(column), ); log('[cost][cellCache]', performance.now() - cellsCacheStart); /** * 对渲染左缓存优化,优化滚动效率,当仅滚动带来的 render,不去重新进行 cells 计算 * 1. 目前识别滚动带来 render 即只有 top 发生变化就属于 */ return h( 'div', { key: id, attrs: { role: BODY_ROW, }, class: { 'grid-row': true, 'row-odd': index % 2 }, style: fixStyle({ transform: top, height, width, }), }, rowCells, ); };