import { IStoreState, ID, ICell, NumericFormat, ICellType } from '../../index.data'; import { getColumnByIndex } from './column'; import { getRowByCell, getRow, getRowi, isTreeRow } from './row'; import { getSize } from './sheet'; import * as R from 'rambda'; import { _getCell } from './base'; import { getTypedId } from '../../utils'; import { ERROR_REF } from '../../formula-parser/error'; import { isExternalCell } from '../formula_support/externalCell'; /** * 根据 cell.id => cell */ export const getCell = _getCell; /** * 使用i(行序) 与 j(列序) 查找单元格 * 已经考虑了合并单元格的情形 */ export function getCellByij( state: IStoreState, i: number, j: number, // 被合并单元格是否追踪到主合并单元格 // 如果指定为true, 那么将返回主单元格 // 如果指定为false, 那么将返回null ( 在计算时可能会用到这种方式 ) digMergeCell = true, // 对于批查找情形, 直接使用外界传递的matrix缓存, 不要自己每次算 _martrixCache?: ICell[][], ): ICell | undefined { const martrix: ICell[][] = _martrixCache || getMatrix(state); if (!digMergeCell) return martrix[i][j]; if (martrix[i][j]) return martrix[i][j]; // 考虑合并单元格 for (let row = i; row >= 0; row--) { for (let col = j; col >= 0; col--) { const cell = martrix[row][col]; if (cell && (cell!.rowspan! >= 2 || cell!.colspan! >= 2)) { return cell; } else if (cell) { break; } } } } // var getMatrixCallCount = 0; export function getMatrix(state: IStoreState) { const { rowCount, colCount } = getSize(state); const martrix: ICell[][] = R.range(0, rowCount).map(_ => R.range(0, colCount).map(_ => null)); // 确保拿到最新的ij const stateWithij = fillij(state); Object.values(stateWithij.cells).forEach(cell => { martrix[cell!.i!][cell!.j!] = cell; }); return martrix; } export function getCellsByijs(state: IStoreState, ijs: { i: number; j: number }[]): ICell[] { const matrix = getMatrix(state); return ijs .map(ij => getCellByij(state, ij.i, ij.j, false, matrix)) .filter(cell => !R.isNil(cell)) as ICell[]; } // 多选时, 很不容易找到selectedids, 所以写了这个方法 export function getSelectedCellIds(state: IStoreState): ID[] { const { selectionType, selectedCellRange } = state; if (selectionType !== 'cell') return []; const startCellId = selectedCellRange[0]; const endCellId = selectedCellRange[1]; if (startCellId === endCellId) return [startCellId]; const startIj = getCellij(state, startCellId); const endIj = getCellij(state, endCellId); const cells: ICell[] = R.flatten( getRangeByIj(state, startIj.i, startIj.j, endIj.i, endIj.j), ) as any; return cells.filter(Boolean).map(it => it!.id); } // 不考虑合并单元格, 直接查 export function getCellByij_immediate(state: IStoreState, i: number, j: number): ICell { const row = state.rows[i]; const cellId = row.cellIds ? row.cellIds[j] : null; return cellId ? state.cells[cellId] : null; } export function getCellsByijs_immediate( state: IStoreState, ijs: { i: number; j: number }[], ): ICell[] { return ijs.map(ij => getCellByij_immediate(state, ij.i, ij.j)).filter(Boolean) as ICell[]; } /** * 注意会返回cell=null, 使用前需要过滤 */ export function getRangeByIj( state: IStoreState, startI: number, startJ: number, endI: number, endJ: number, ): ICell[][] { const minI = Math.min(startI, endI); const maxI = Math.max(startI, endI); const minJ = Math.min(startJ, endJ); const maxJ = Math.max(startJ, endJ); return R.range(minI, maxI + 1).map(i => R.range(minJ, maxJ + 1).map(j => getCellByij_immediate(state, i, j)), ); } /** * 如果这个方法做到initStoreState里, 新增的行就要另外考虑, 所以为开发进度, 暂时这样做 * 根据 cell.id => cellType ( numeric | ...) */ export function getCellType(state: IStoreState, id: ID): ICellType { const cell = getCell(state, id)!; if (cell.type) return cell.type; // const row = getRow(state, cell.rowId!); // if (row.type === 'header' || row.type === 'footer') { // // 如果是header, 则直接返回text // return 'text'; // } else { // 否则找到相应列的类型并返回 const { j } = getCellij(state, cell!.id); const column = getColumnByIndex(state, j)!; return column.type || 'text'; // } } /** * 如果这个方法做到initStoreState里, 新增的行就要另外考虑, 所以为开发进度, 暂时这样做 */ export function getCellFormat(state: IStoreState, id: ID): NumericFormat | undefined { const cell = getCell(state, id)!; if (cell.format) return cell.format; const row = getRow(state, cell.rowId!); if (row.type === 'body') { const { j } = getCellij(state, cell!.id); const column = getColumnByIndex(state, j); return column.format; } } /** * 是否是编号类型单元格 */ export function isRowNoCell(state: IStoreState, id: ID): boolean { const cell = getCell(state, id); if (!cell) return false; const { j } = getCellij(state, id); const row = getRowByCell(state, id); return row.type === 'body' && (cell!.type || state.columns[j].type) === 'row-no'; } /** * 获取编号类型单元格的value * 需要保证 isRowNoCell(cellId) === true */ export function getRowNoCellValue( state: IStoreState, id: ID, ): { isLeaf: boolean; collapsed: boolean; path: number[]; } | null { const cell = getCell(state, id)!; if (!cell) return null; return getRowNoCellValueByRow(state, cell.rowId!); } export function getRowNoCellValueByRow(state: IStoreState, rowId: ID) { const row = getRow(state, rowId)!; if (!isTreeRow(row)) throw new Error('getRowNoCellValueByRow出错: 当前行不在tree area, 暂不支持计算rowNo'); // 找到第一行body的位置, 将跳过header, 因为header不需要计算rowNo const firstTreeRow = state.rows.find(isTreeRow)!; const skip = getRowi(state, firstTreeRow.id); const path = [...state.treeContext[rowId!].path]; return { isLeaf: state.treeContext[rowId!].childrenIds.length === 0, collapsed: state.collapsedRowIds.includes(row.id), path: [path[0] - skip, ...path.slice(1)], }; } /** * 单元格是否可以编辑 */ export function isCellEditable(state: IStoreState, id: ID): boolean { const cellType = getCellType(state, id); return cellType !== 'row-no'; } /** * 获取右侧第i个cell * 用途: 比如取价格单元格右侧的备注单元格 * 注意: 如果存在跨列, 则返回null */ export function getRightCell(state: IStoreState, id: ID, span = 1) { const cell = getCell(state, id); if (cell) { const { i, j } = getCellij(state, id); var rightCell = getCellByij(state, i, j + span); // 对于跨列情形, 返回的rightCell === 当前cell if (rightCell && rightCell.id !== id) { return rightCell; } } return null; } // 不考虑rowspan/colspan export function getCellij(state: IStoreState, _cellId: ID): { i: number; j: number } { // console.log('-------------------'); // console.log(state); // console.log(state.rows); const cellId = getTypedId(_cellId + ''); const rowId = state.cells[cellId]!.rowId as any; const rowIndex = getRowi(state, rowId); const row = state.rows[rowIndex]; return { i: rowIndex, j: row.cellIds!.indexOf(cellId), }; } export function fillij(state: IStoreState): IStoreState { const rows = state.rows.map((row, i) => Object.assign({}, row, { i })); const cells = R.map(cell => Object.assign({}, cell, getCellij(state, cell!.id)), state.cells); return { ...state, rows, cells, }; }