import { ICommand } from 'valor-unistore-undo'; import * as R from 'rambda'; import { IStoreState, IRow, IColumn, ICellNN, ID, ICell } from '../index.data'; import { SpreadSheetRuntime } from '../RuntimeContext'; import { insertColumn } from '../store/actions/column'; import { dropIndex, dissoc, idMap } from 'valor-app-utils'; import { recalculate } from '../store/actions/recalculate'; import { IRowWithCell } from '../SpreadSheetProvider/index.data'; export interface InsertColumnCommandParams { j: number; column: IColumn; // 如果左侧合并单元格, 例如: // ------------------- // | | | | // ------------------- // | | | <== 第2列被合并 // 如果在右侧插列, 那么, 如果forceMergePolicy='left', 则将形成: // ------------------------- // | | | | | // ------------------------- // | | | <== 第2列与新插入的列的单元格被合并 forceMerge?: 'left' | null; // 单元格来源1: 外部提供模板, 有多少行就有多少cell, 一般设置value和formula即可 // 注意: 由于总是先复制左侧列的单元格, 所以cells不一定完全与左侧列同形!!! 一般而言, 应是左侧列单元格的子集 // 详细解释参见 insertColumn 方法内部注释 cells?: Partial[]; // 单元格来源2: 1. 如果未提供cells, 则从左侧单元格复制; 2. 如果提供cells, 则从左侧单元格复制shape, 然后从cells复制内容 copyAdjCellAttrs?: ('value' | 'formula' | 'locked')[]; } interface IUndoContext { // 合并主单元格的colspan将被修改, 在撤销时直接用它替换回去 updatedStaleCells: ICellNN[]; } class InsertColumnCommand extends ICommand< IStoreState, InsertColumnCommandParams, IUndoContext, SpreadSheetRuntime > { execute = () => { const state = this.store.getState(); const oldColumnLength = state.columns.length; if (this.params.j > oldColumnLength) throw new Error(`j的长度范围: [0, ${oldColumnLength}], 但当前为${this.params.j}`); const result = insertColumn( state, this.params.j, this.params.column, this.params.cells || [], this.params.forceMerge || null, this.params.copyAdjCellAttrs || [], this.runtime!, ); this.store.setState(Object.assign(result.newState, { readyDimensions: true }) as any); this.undoContext = { updatedStaleCells: result.updatedStaleCells }; setTimeout(() => { this.runtime!.setSheetDimensions(); }); return true; }; undo = () => { const state = this.store.getState(); const { newState } = undoInsertColumn( state, this.params.j, this.undoContext.updatedStaleCells, this.runtime!, ); this.store.setState(Object.assign(newState, { readyDimensions: true }) as any); setTimeout(() => { this.runtime!.setSheetDimensions(); // 暂时默认选中第一个单元格, 否则会抛异常 this.runtime!.fsmService.send({ type: 'SELECT.CELL', selectedCell: state.rows[0].cellIds![0]!, }); }); }; } export function undoInsertColumn( state: IStoreState, j: number, updatedStaleCells: ICellNN[], runtime: SpreadSheetRuntime, ) { const cellIdsInJ = state.rows.map(row => row.cellIds![j]).filter(Boolean); const updatedColumns = [ ...state.columns.slice(0, j), ...state.columns.slice(j + 1).map(it => ({ ...it, j: it.j! - 1 })), ]; const updatedRows = state.rows.map(row => Object.assign({}, row, { cellIds: dropIndex(row.cellIds!, j) }), ); const newStateAfterUndo = { rows: updatedRows, columns: updatedColumns, cells: Object.assign( {}, dissoc(state.cells, cellIdsInJ.map(it => it + '')), idMap(updatedStaleCells), ), }; const calculatedStatePatch = runtime.batchMode ? Object.assign({}, newStateAfterUndo) : recalculate( { ...state, ...newStateAfterUndo }, { force: true, config: runtime && runtime.formulaManager.config }, ); return { newState: { rows: updatedRows, columns: updatedColumns, ...calculatedStatePatch }, }; } export default InsertColumnCommand;