import { Store } from '@/lib/store'; import { CellType, CellValue, EditingData, GridData, GridProp, IndexColumn, IndexRow, RowData, } from '@/lib/types'; import { forEach, isFunction, isNumber, isString, some } from 'lodash'; import { markRaw } from '@vue/composition-api'; import { parseWatcherFn } from '@/lib/utils/vues'; import { ID_KEY, TREE_PARENT_ID_KEY } from '@/lib/constants/default-props'; import { dfsTree } from '@/lib/utils/tree'; import { EditOperator } from '@/lib/operator/EditOperator'; import { RowModifyInfo } from '@/lib/types/api'; import { RowSelectionOperator } from '@/lib/operator/RowSelectionOperator'; /** * 管理整个表格数据的编辑、计算 主要操作 data2D */ export class RowDataCalculator { private store: Store; private readonly editOperator: EditOperator; private readonly rowSelectionOperator: RowSelectionOperator; constructor( store: Store, editOperator: EditOperator, rowSelectionOperator: RowSelectionOperator, ) { this.store = store; this.editOperator = editOperator; this.rowSelectionOperator = rowSelectionOperator; } public modifyRows(changes: RowModifyInfo[]) { this.editOperator.modifyData2D(changes); this.flushRows(changes.map((item) => item.rowId)); } public removeRows(rowIds: string[]) { // TODO 对于树结点及联删除 this.store.mutation.deleteRows(rowIds); // 删除了行需要重写计算列总计 this.editOperator.flushColumnTotal(); const viewRowRecord = this.store.state.view.rowRecord; const rowHeight = this.store.state.config.rowHeight; // 重新计算 row 的高度 const rowIndex2Id = []; let indexCount = 0; let topCount = 0; const newRows = Object.keys(viewRowRecord).reduce((result, currentRowId) => { const currentRow = viewRowRecord[currentRowId]; if (!rowIds.includes(currentRowId)) { result[currentRowId] = { ...currentRow, top: topCount, index: indexCount, }; rowIndex2Id.push(currentRowId); indexCount = indexCount + 1; topCount = topCount + rowHeight; } return result; }, {}); this.store.mutation.setViewRow(newRows, rowIndex2Id); } public flushPropData( data: GridProp['data'], { idKey, childrenKey, expandKey, defaultExpand, }: { idKey: string; childrenKey: string; expandKey: string; isTree: boolean; defaultExpand: boolean; }, ) { const rowHeight = this.store.state.config.rowHeight; const columnRecord = this.store.state.index.columnRecord; return this.flushRowByTree(data, columnRecord, rowHeight, { idKey: idKey || ID_KEY, childrenKey, expandKey, defaultExpand, }); } /** * 刷新对于行的信息,包括(calculate、rowSelection、CellType) * @param rowIds * @private */ private flushRows(rowIds: string[]) { forEach(rowIds, (id) => { this.editOperator.flushByRow(id, true); this.rowSelectionOperator.flushRowSelectionById(id); }); } private flushRowByTree( allRowData: GridProp['data'], indexColumns: Record, rowHeight: number, { idKey, expandKey, childrenKey, defaultExpand, }: { idKey: string; expandKey: string; childrenKey: string; defaultExpand: boolean; }, ) { // 定义要收集的数据集 const rowRecord: Record = {}; const rowIndex2Id = [] as string[]; const data2D: Record = {}; // 定义累计器 let treeIndexCount = 0; // 列收集工具 const accumulate = (rowId: string, currentRowRecord: IndexRow) => { rowIndex2Id.push(rowId); treeIndexCount = treeIndexCount + 1; rowRecord[rowId] = currentRowRecord; }; // 表格数据收集工具 const buildData2DCollector = (rowId) => (data) => { data2D[rowId] = data; }; // 列标识收集工具 const collectRowRecord = ( isTree: boolean, originGridRowData: GridData, parentRowRecord: IndexRow, treePath: string[], ) => { const rowId = this.getRowId(originGridRowData, idKey); if (isTree) { const parentRowId = (parentRowRecord as IndexRow)?.id; const hasChildren = (originGridRowData[childrenKey] as any)?.length > 0; const currentRowRecord = { id: rowId, index: treeIndexCount, parentId: parentRowId, treePath: treePath, expand: originGridRowData[expandKey] === undefined ? defaultExpand : !!originGridRowData[expandKey], hasChildren, childrenIds: [], top: treeIndexCount * rowHeight, height: rowHeight, }; if (parentRowRecord) { parentRowRecord.childrenIds.push(rowId); // 遍历列收集信息 this.collectDataInfo( buildData2DCollector(rowId), indexColumns, originGridRowData, parentRowId, this.collectNodeCollector(data2D[parentRowId]), ); } else { // 遍历列收集信息 this.collectDataInfo( buildData2DCollector(rowId), indexColumns, originGridRowData, ); } if (hasChildren) { this.clearCollectorNode(data2D[rowId]); } accumulate(rowId, currentRowRecord); return currentRowRecord; } }; dfsTree(allRowData, idKey, childrenKey, (node, _parent, parentResult, path) => { return collectRowRecord(true, node, parentResult as IndexRow, path); }); return { rowRecord, rowIndex2Id, data2D, }; } /** * 计算所有的数据,形成初始化的数据集,计算顺序 原始值填充 > 树结点收集 > calculator > rowSelection | total * * @param data2DCollector * @param indexColumns * @param originRowData * @param hasChildren * @param parentId * @param collectTreeData * @private */ private collectDataInfo( data2DCollector: (rowData: RowData) => void, indexColumns: Record, originRowData: Record, parentId?: string, collectTreeData?: (indexColumn: IndexColumn, value: CellValue) => void, ) { const fieldData: Record = {}; const treeDataCollector = collectTreeData; // 遍历列 this.traversalColumns4Collect(originRowData, { fieldDataCollector: (rowId, data) => { fieldData[rowId] = data; return data; }, treeDataCollector, }); data2DCollector({ fieldData: fieldData, otherData: markRaw(this.buildRowOtherData(parentId, originRowData, indexColumns)), }); } private clearCollectorNode = (parentRowData: RowData) => { // 清空 nodeCollect 的数据 forEach(parentRowData.fieldData, (data2D) => { if (data2D.type === CellType.nodeCollector) { data2D.value = ''; } }); }; private collectNodeCollector = (parentRowData: RowData) => { return (column: IndexColumn, childrenValue: CellValue) => { if (parentRowData?.fieldData?.[column.id].type === CellType.nodeCollector) { const reduce = column.nodeCollector; if (isFunction(reduce)) { const reduceResult = reduce( parentRowData.fieldData[column.id].value, childrenValue, ); parentRowData.fieldData[column.id].value = reduceResult; } } }; }; private traversalColumns4Collect( originRowData: GridData, { fieldDataCollector, treeDataCollector, }: { fieldDataCollector: (columnId: string, fieldData: EditingData) => EditingData; treeDataCollector: (column: IndexColumn, value: CellValue) => void; }, ) { const columnRecord = this.store.state.index.columnRecord; forEach(columnRecord, (column) => { const data = fieldDataCollector(column.id, this.buildFieldData(column, originRowData)); // 收集的是没有子节点的 if (data.type !== CellType.nodeCollector && treeDataCollector) { treeDataCollector(column, data.value); } }); } private buildFieldData = (column: IndexColumn, originRowData: GridData) => { const type = (isFunction(column.cellType) ? column.cellType(originRowData) : column.cellType) || CellType.viewer; // 树结点要总计后再计算 const value = type === CellType.calculator ? parseWatcherFn(column.calculator)(originRowData) : originRowData[column.field]; return { type, value, originValue: originRowData[column.field], tip: '', error: '', }; }; private buildRowOtherData = ( parentId: string, originRowData: GridData, indexColumns: Record, ) => { const otherData = {}; if (parentId !== undefined) otherData[TREE_PARENT_ID_KEY] = parentId; Object.keys(originRowData).forEach((key) => { if (some(indexColumns, (item) => item.field === key)) return; otherData[key] = originRowData[key]; }); return otherData; }; private getRowId = (rowData: Record, idKey: string) => { const id = rowData[idKey]; if (isString(id) || isNumber(id)) { return `${id}`; } else { throw new Error('NO Match id in table data'); } }; }