import { Store } from '@/lib/store'; import { RowModifyInfo } from '@/lib/types/api'; import { forEach, invert, reduce, isFunction } from 'lodash'; import { CellType, CellValue } from '@/lib/types'; import { nextTick } from '@vue/composition-api'; import { getCurrentRowData } from '@/lib/calculate'; import { parseWatcherFn } from '@/lib/utils/vues'; import { parseCellType } from '@/lib/utils/cell'; /** * 管理整个表格数据的编辑、计算 主要操作 data2D */ export class EditOperator { private store: Store; constructor(store: Store) { this.store = store; } public modifyData2D(changes: RowModifyInfo[]) { const field2Id = invert(this.store.state.index.columnId2Field); const changePath2Value = {}; changes.forEach(({ rowId, modifyRowData }) => { forEach(modifyRowData, (cellValue, key) => { if (field2Id[key]) { changePath2Value[`${rowId}.${field2Id[key]}`] = cellValue; } }); }); this.batchModifyCell(changePath2Value); } public flushCalculator() { const columnRecord = this.store.state.index.columnRecord; const columnId2Field = this.store.state.index.columnId2Field; const data2D = this.store.state.index.data2D; forEach(data2D, (rowData, rowId) => { const formattedRowData = getCurrentRowData(rowData, columnId2Field); forEach(columnRecord, (indexColumn, columnId) => { const currentData = rowData.fieldData[columnId]; if (currentData.type === CellType.calculator) { const value = parseWatcherFn(indexColumn.calculator)(formattedRowData); this.store.mutation.inputEditingValue(rowId, columnId, value); } }); }); } public flushColumnTotal() { const columnRecord = this.store.state.index.columnRecord; const rowRecord = this.store.state.index.rowRecord; const data2D = this.store.state.index.data2D; const columnTotal: Record = {}; const columnId2Field = this.store.state.index.columnId2Field; forEach(data2D, (rowData, rowId) => { const currentRow = rowRecord[rowId]; forEach(rowData.fieldData, (filedData, columnId) => { const totalCollector = columnRecord[columnId].totalCollector; if (isFunction(totalCollector)) { columnTotal[columnId] = totalCollector(columnTotal[columnId], { currentValue: filedData.value, rowData: getCurrentRowData(rowData, columnId2Field), hasChildren: currentRow.hasChildren, hasParent: !!currentRow.parentId, }); } }); }); this.syncColumnTotal(columnTotal); } /** * 刷新数据(刷新 计算结果、刷新单元格类型) * @param rowId * @param isRunLink 是否触发联动 */ public flushByRow(rowId: string, isRunLink = false) { const columnRecord = this.store.state.index.columnRecord; const currentRowData2D = this.store.state.index.data2D[rowId]; const columnId2Field = this.store.state.index.columnId2Field; const formattedRowData = getCurrentRowData(currentRowData2D, columnId2Field); forEach(columnRecord, (indexColumn, columnId) => { const currentType = parseCellType(indexColumn.cellType, formattedRowData); this.store.mutation.changeData2DType(rowId, columnId, currentType); // TODO 解除耦合,此处刷新后有其他问题,需要使用内部封装方法 if (currentType === CellType.calculator) { const value = parseWatcherFn(indexColumn.calculator)(formattedRowData); this.store.mutation.inputEditingValue(rowId, columnId, value); if (isRunLink) { const oldValue = currentRowData2D.fieldData[columnId].value; this.collectByCurrentData(oldValue, value, rowId, columnId); } } }); } public syncColumnTotal(columnTotal: Record) { forEach(columnTotal, (totalValue, columnId) => { this.store.mutation.setColumnTotal(columnId, totalValue); }); } /** * 修改计算单元(伴随联动)) * * @param rowId * @param columnId * @param value */ public calculateCell(rowId: string, columnId: string, value: CellValue) { const currentData = this.getCurrentData(rowId, columnId); const currentValue = currentData?.value; if (currentData.type === CellType.calculator && value !== currentValue) { this.store.mutation.inputEditingValue(rowId, columnId, value); nextTick(() => this.collectByCurrentData(currentValue, value, rowId, columnId)); } } public modifyCell(rowId: string, columnId: string, value: CellValue) { const currentData = this.getCurrentData(rowId, columnId); const currentValue = currentData?.value; if (value === currentValue) return; this.store.mutation.inputEditingValue(rowId, columnId, value); nextTick(() => this.collectByCurrentData(currentValue, value, rowId, columnId)); } public batchModifyCell(changeInfo: Record) { Object.keys(changeInfo).forEach((path) => { const value = changeInfo[path]; const [rowId, columnId] = path.split('.'); this.modifyCell(rowId, columnId, value); }); } public inputCell(rowId: string, columnId: string, value: CellValue) { // 渲染结束后做列总计的变化 const currentData = this.getCurrentData(rowId, columnId); const currentValue = currentData?.value; if (currentData.type === CellType.editor && value !== currentValue) { this.store.mutation.inputEditingValue(rowId, columnId, value); nextTick(() => this.collectByCurrentData(currentValue, value, rowId, columnId)); } } public batchInputCell(changeInfo: Record) { Object.keys(changeInfo).forEach((path) => { const value = changeInfo[path]; const [rowId, columnId] = path.split('.'); this.inputCell(rowId, columnId, value); }); } private getCurrentData(rowId: string, columnId: string) { return this.store.state.index.data2D[rowId]?.fieldData?.[columnId]; } private async collectByCurrentData( oldValue: CellValue, currentValue: CellValue, rowId: string, columnId: string, ) { console.log('collectByCurrentData', columnId, rowId, oldValue, currentValue); // 优先计算所有 Node 信息,再对列进行求和 await nextTick(() => this.collectTreeNodes(rowId, columnId)); nextTick(() => this.collectTotal(rowId, columnId)); } private collectTotal(_rowId: string, columnId: string) { const totalCollector = this.store.state.index.columnRecord[columnId].totalCollector; if (totalCollector) { return this.countColumn(columnId, totalCollector); } } /** * 对于配置了对子做总计的结点做求和 * @param rowId * @param columnId * @private */ private collectTreeNodes(rowId: string, columnId: string) { const currentParentRowId = this.store.state.index.rowRecord[rowId].parentId; // 找到当前树根结点 if (currentParentRowId) { this.collectParentCell(currentParentRowId, columnId); } } private collectParentCell(rowId: string, columnId: string) { const parentData = this.getCurrentData(rowId, columnId); // 父节点 为 收集者 则触发收集 if (parentData.type === CellType.nodeCollector) { const childRowIds = this.store.state.index.rowRecord[rowId].childrenIds; const nodeReduce = this.store.state.index.columnRecord[columnId].nodeCollector; if (isFunction(nodeReduce)) { const oldValue = this.getCurrentData(rowId, columnId)?.value || 0; // 遍历求和 const result = reduce( childRowIds, (result, id) => { const currentValue = this.getCurrentData(id, columnId)?.value || 0; return nodeReduce(result, currentValue); }, 0, ); this.store.mutation.inputEditingValue(rowId, columnId, result); nextTick(() => this.collectByCurrentData(oldValue, result, rowId, columnId)); } } } private countColumn( columnId: string, counter: ( total: CellValue | Object, data: { rowData: any; currentValue: CellValue; hasChildren: boolean; hasParent: boolean; }, ) => CellValue | Object, ) { const data2D = this.store.state.index.data2D; const rowRecord = this.store.state.index.rowRecord; const result = reduce( data2D, (total, rowData, rowId) => { return counter(total, { rowData: getCurrentRowData(rowData, this.store.state.index.columnId2Field), currentValue: rowData.fieldData?.[columnId].value, hasChildren: rowRecord[rowId].hasChildren, hasParent: !!rowRecord[rowId].parentId, }); }, 0, ); console.log(columnId, '_', result); this.store.mutation.setColumnTotal(columnId, `${result}`); } }