import { SelectingOperator } from '@/lib/operator/SelectingOperator'; import { BodyClickedOperator } from '@/lib/operator/BodyClickedOperator'; import { DragDirection } from '@/lib/types'; import { calculateSelectionArea } from '@/lib/calculate/area'; import { GroupPin, GroupSelection } from '@/lib/types/group'; import { getChangeDataInfo, getRowOrColumnFillingDataPath, singleAreaData2RowColumnArray, } from '@/lib/calculate'; import { highlightAnimationByClass } from '@/lib/utils/style'; import { CELL_CALCULATING } from '@/lib/constants/dom-class'; import { getMinAndMax } from '@/lib/utils/math'; import { getMinAndMaxColumn } from '@/lib/utils/row-column'; import { Store } from '@/lib/store'; import { BodyMenuOperator } from '@/lib/operator/BodyMenuOperator'; import { EditOperator } from '@/lib/operator/EditOperator'; import {log} from "@/lib/utils/log"; export class CalculatingOperator { private readonly store: Store; private readonly selectingOperator: SelectingOperator; private readonly clickedOperator: BodyClickedOperator; private readonly bodyMenuOperator: BodyMenuOperator; private readonly editOperator: EditOperator; private lastUpdateRowId: string; private lastUpdateColumnId: string; private beginRowId: string; private beginColumnId: string; private baseColumnIndex: [number, number]; private baseRowIndex: [number, number]; private basePin: GroupPin; constructor( store: Store, selectingOperator: SelectingOperator, clickedOperator: BodyClickedOperator, bodyMenuOperator: BodyMenuOperator, editOperator: EditOperator, ) { this.store = store; this.selectingOperator = selectingOperator; this.clickedOperator = clickedOperator; this.bodyMenuOperator = bodyMenuOperator; this.editOperator = editOperator; this.lastUpdateColumnId = ''; this.lastUpdateRowId = ''; } public initCalculating(beginRowId: string, beginColumnId: string) { this.silentEnvironment(); this.beginColumnId = beginColumnId; this.beginRowId = beginRowId; const { column, row, pin } = this.clickedOperator.isSomeCellClicked() ? this.clickedOperator.getClickedArea() : // 目前仅支持单区域计算 this.selectingOperator.getHitAreaWhenSingleHit(); this.baseColumnIndex = column; this.baseRowIndex = row; this.basePin = pin; this.store.mutation.triggerCalculationChanging(true); } public dragging(columnId: string, rowId: string) { if (columnId === this.lastUpdateColumnId && rowId === this.lastUpdateRowId) return; // 如果移动区域到的区域无法编辑则终止 this.lastUpdateColumnId = columnId; this.lastUpdateRowId = rowId; const currentColumnIndex = this.store.state.index.columnRecord[columnId].index; const currentRowIndex = this.store.state.view.rowRecord[rowId].index; const calculatingArea = this.getCalculatingArea(currentColumnIndex, currentRowIndex); // 当前移动位置单元格在选择区域中时则 清空当前画的区域 if (!calculatingArea) return this.cleatDrawing(); // 判断移动方向是不是在可拖拽范围内 const beginColumnHandleConfig = this.store.state.index.columnRecord[this.beginColumnId].fillHandle; if ( Array.isArray(beginColumnHandleConfig) && !beginColumnHandleConfig.includes(calculatingArea.direction) ) { return; } const { startRowIndex, endRowIndex, startColumnIndex, endColumnIndex, direction } = calculatingArea; this.drawing(startColumnIndex, endColumnIndex, startRowIndex, endRowIndex, direction); } public calculate() { const fillResult = this.fillData2CalculatingArea(); if (!fillResult) return; this.store.mutation.triggerCalculationChanging(false); this.store.mutation.destroyDragCalculate(); // 对于激活单元格计算的需要移除并转化为选中 if (this.clickedOperator.isSomeCellClicked()) { this.clickedOperator.inactiveClicked(); this.selectingOperator.activeSelection(this.beginColumnId, this.beginRowId); } else { // 以当前计算拖拽方向的反方向点作为初始激活点 const { columnId, rowId } = this.getDivergenceSelectingPoint(); this.selectingOperator.activeSelection(columnId, rowId); } const { columnId: endColumnId, rowId: endRowId } = this.getDivergencePoint(); log('[operator][calculate]', `${endColumnId}_${endRowId}`); this.selectingOperator.updateSelection(endColumnId, endRowId); this.selectingOperator.finishSelection(); } private cleatDrawing() { this.store.mutation.destroyDragCalculate(); } private drawing( startColumnIndex: number, endColumnIndex: number, startRowIndex: number, endRowIndex: number, direction: DragDirection, ) { this.store.mutation.activeDragCalculate( startColumnIndex, startRowIndex, this.basePin, direction, ); this.store.mutation.updateDragCalculate(endColumnIndex, endRowIndex, this.basePin); const { leftArea, rightArea, scrollArea } = calculateSelectionArea( this.store.state.index.columnGroupRecord.left.ids.length, this.store.state.index.columnGroupRecord.scroll.ids.length, )(this.store.state.dragCalculateStatus); this.store.mutation.updateDragCalculateArea({ leftArea, rightArea, scrollArea }); } private getDivergenceSelectingPoint() { // 根据当前拖拽方向确定背离点 if (this.store.state.dragCalculateStatus.direction === DragDirection.left) { return this.selectingOperator.getTopRightCellWhenSingleHit(); } if (this.store.state.dragCalculateStatus.direction === DragDirection.right) { return this.selectingOperator.getTopLeftCellWhenSingleHit(); } if (this.store.state.dragCalculateStatus.direction === DragDirection.top) { return this.selectingOperator.getBottomLeftCellWhenSingleHit(); } return this.selectingOperator.getTopLeftCellWhenSingleHit(); } private getDivergencePoint() { // 根据当前拖拽方向确定背离点 if (this.store.state.dragCalculateStatus.direction === DragDirection.left) { return this.getBottomLeftCell(); } if (this.store.state.dragCalculateStatus.direction === DragDirection.right) { return this.getBottomRightCell(); } if (this.store.state.dragCalculateStatus.direction === DragDirection.top) { return this.getTopRightCell(); } return this.getBottomRightCell(); } private getAngleCell() { const [minRowIndex, maxRowIndex] = getMinAndMax( this.store.state.dragCalculateStatus.startRowIndex, this.store.state.dragCalculateStatus.endRowIndex, ); const [minColumn, maxColumn] = getMinAndMaxColumn( this.store.state.dragCalculateStatus.startColumn, this.store.state.dragCalculateStatus.endColumn, ); return { minRowIndex, maxRowIndex, minColumn, maxColumn, }; } private getBottomLeftCell() { const { minColumn, maxRowIndex } = this.getAngleCell(); return { columnPin: minColumn.pin, columnIndex: minColumn.index, columnId: this.store.state.index.columnGroupRecord[minColumn.pin].ids[minColumn.index], rowIndex: maxRowIndex, rowId: this.store.state.view.rowIndex2Id[maxRowIndex], }; } private getBottomRightCell() { const { maxColumn, maxRowIndex } = this.getAngleCell(); return { columnPin: maxColumn.pin, columnIndex: maxColumn.index, columnId: this.store.state.index.columnGroupRecord[maxColumn.pin].ids[maxColumn.index], rowIndex: maxRowIndex, rowId: this.store.state.view.rowIndex2Id[maxRowIndex], }; } private getTopRightCell() { const { maxColumn, minRowIndex } = this.getAngleCell(); return { columnPin: maxColumn.pin, columnIndex: maxColumn.index, columnId: this.store.state.index.columnGroupRecord[maxColumn.pin].ids[maxColumn.index], rowIndex: minRowIndex, rowId: this.store.state.view.rowIndex2Id[minRowIndex], }; } private getCalculatingArea(currentColumnIndex: number, currentRowIndex: number) { if ( currentRowIndex >= this.baseRowIndex[0] && currentRowIndex <= this.baseRowIndex[1] && currentColumnIndex >= this.baseColumnIndex[0] && currentColumnIndex <= this.baseColumnIndex[1] ) return null; if (currentRowIndex < this.baseRowIndex[0]) { // 上方 return { direction: DragDirection.top, startRowIndex: this.baseRowIndex[0] - 1, startColumnIndex: this.baseColumnIndex[0], endRowIndex: currentRowIndex, endColumnIndex: this.baseColumnIndex[1], }; } // 下方 if (currentRowIndex > this.baseRowIndex[1]) { return { direction: DragDirection.bottom, startRowIndex: this.baseRowIndex[1] + 1, startColumnIndex: this.baseColumnIndex[0], endRowIndex: currentRowIndex, endColumnIndex: this.baseColumnIndex[1], }; } // 左侧 if (currentColumnIndex < this.baseColumnIndex[0]) { return { direction: DragDirection.left, startRowIndex: this.baseRowIndex[0], startColumnIndex: this.baseColumnIndex[0] - 1, endRowIndex: this.baseRowIndex[1], endColumnIndex: currentColumnIndex, }; } // 右侧 return { direction: DragDirection.right, startRowIndex: this.baseRowIndex[0], startColumnIndex: this.baseColumnIndex[1] + 1, endRowIndex: this.baseRowIndex[1], endColumnIndex: currentColumnIndex, }; } private getCalculatingHitArea() { return this.store.state.dragCalculateStatus.leftArea.hit ? this.store.state.dragCalculateStatus.leftArea : this.store.state.dragCalculateStatus.scrollArea.hit ? this.store.state.dragCalculateStatus.scrollArea : this.store.state.dragCalculateStatus.rightArea; } private fillData2CalculatingArea() { // 目前拖动计算不支持分区 const calculatingArea = this.getCalculatingHitArea(); // 拖动过程中取消的无需计算 if (calculatingArea.row?.length !== 2 || calculatingArea.column?.length !== 2) return null; // 单击是选中单击区域否则使用拖拽 const selectionArea = this.clickedOperator.isSomeCellClicked() ? this.clickedOperator.getClickedSelection() : this.selectingOperator.getHitSelectionWhenSingleHit(); // 使用 batchInput 非editor 的单元格不会被修改 this.editOperator.batchInputCell( this.calculateCalculateData( this.store.state.dragCalculateStatus.direction, calculatingArea, selectionArea, ), ); return highlightAnimationByClass(CELL_CALCULATING, 'cellCalculate 1s'); } private silentEnvironment() { this.bodyMenuOperator.inactive(); } private calculateCalculateData( direction: DragDirection, calculatingArea: GroupSelection, selectionArea: GroupSelection, ) { const index2RowId = this.store.state.view.rowIndex2Id; const data2d = this.store.state.index.data2D; const columnRecord = this.store.state.index.columnRecord; // 获取当前对于索引区域 const groupPin = this.clickedOperator.isSomeCellClicked() ? this.clickedOperator.getClickedPin() : this.selectingOperator.getGroupPinWhenSingleHit(); // 当前区域 的 列索引 const index2Column = groupPin === GroupPin.left ? this.store.state.index.columnGroupRecord.left.ids : groupPin === GroupPin.scroll ? this.store.state.index.columnGroupRecord.scroll.ids : this.store.state.index.columnGroupRecord.right.ids; // 获取所有 selection 的二维数组,第一层 row 第二次 column const rowColumnData = singleAreaData2RowColumnArray(selectionArea)( index2RowId, index2Column, data2d, ); // 获取填充 data 索引 const dataPaths = getRowOrColumnFillingDataPath( columnRecord, index2RowId, index2Column, calculatingArea, ); // 根据处理方向处理数据 return getChangeDataInfo(rowColumnData, dataPaths, direction); } }