объединенные ячейки
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
} from 'react'
|
||||
import { VariableSizeGrid } from 'react-window'
|
||||
import { useMultiSheetEngine } from '../../hooks/useMultiSheetEngine'
|
||||
import { MergedCell } from '../../types/template'
|
||||
|
||||
const ROW_COUNT = 90
|
||||
const COL_COUNT = 20
|
||||
@@ -26,9 +27,59 @@ type SheetType = keyof typeof SHEET_NAMES
|
||||
// --- Helper Functions ---
|
||||
const getColumnLabel = (index: number) => String.fromCharCode(65 + index)
|
||||
|
||||
// Функция для преобразования адреса ячейки в координаты
|
||||
const cellAddressToCoords = (address: string): { row: number; col: number } => {
|
||||
const match = address.match(/^([A-Z]+)(\d+)$/)
|
||||
if (!match) return { row: 0, col: 0 }
|
||||
|
||||
const col = match[1].charCodeAt(0) - 65 // Простое преобразование для одной буквы
|
||||
const row = parseInt(match[2]) - 1
|
||||
|
||||
return { row, col }
|
||||
}
|
||||
|
||||
// Функция для проверки, является ли ячейка частью объединенной группы
|
||||
const findMergedCellInfo = (
|
||||
row: number,
|
||||
col: number,
|
||||
mergedCells: MergedCell[],
|
||||
): {
|
||||
isMerged: boolean
|
||||
isMainCell: boolean
|
||||
mergedCell?: MergedCell
|
||||
spans?: { rowSpan: number; colSpan: number }
|
||||
} => {
|
||||
for (const merged of mergedCells) {
|
||||
const fromCoords = cellAddressToCoords(merged.from)
|
||||
const toCoords = cellAddressToCoords(merged.to)
|
||||
|
||||
// Проверяем, попадает ли текущая ячейка в диапазон объединения
|
||||
if (
|
||||
row >= fromCoords.row &&
|
||||
row <= toCoords.row &&
|
||||
col >= fromCoords.col &&
|
||||
col <= toCoords.col
|
||||
) {
|
||||
const isMainCell = row === fromCoords.row && col === fromCoords.col
|
||||
const rowSpan = toCoords.row - fromCoords.row + 1
|
||||
const colSpan = toCoords.col - fromCoords.col + 1
|
||||
|
||||
return {
|
||||
isMerged: true,
|
||||
isMainCell,
|
||||
mergedCell: merged,
|
||||
spans: { rowSpan, colSpan },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { isMerged: false, isMainCell: false }
|
||||
}
|
||||
|
||||
// --- Interfaces ---
|
||||
interface DualSpreadsheetProps {
|
||||
templateData?: Record<string, Record<string, any>>
|
||||
mergedCells?: MergedCell[] // Добавляем поддержку объединенных ячеек
|
||||
}
|
||||
|
||||
// Кэшированные данные ячеек для оптимизации
|
||||
@@ -45,6 +96,7 @@ interface CellProps {
|
||||
rowIndex: number
|
||||
colIndex: number
|
||||
columnWidth: number
|
||||
columnWidths: number[] // Добавляем массив всех ширин колонок
|
||||
isSelected: boolean
|
||||
isEditing: boolean
|
||||
displayValue: string // Отображаемое значение (вычисленное)
|
||||
@@ -58,6 +110,14 @@ interface CellProps {
|
||||
setActiveSheet: (type: SheetType) => void
|
||||
activeCellRef: React.RefObject<HTMLInputElement>
|
||||
style: CSSProperties // Добавляем style для react-window
|
||||
mergedCellInfo?: {
|
||||
// Информация об объединенных ячейках
|
||||
isMerged: boolean
|
||||
isMainCell: boolean
|
||||
rowSpan?: number
|
||||
colSpan?: number
|
||||
mergedValue?: any
|
||||
}
|
||||
}
|
||||
|
||||
const Cell = memo(
|
||||
@@ -66,6 +126,7 @@ const Cell = memo(
|
||||
rowIndex,
|
||||
colIndex,
|
||||
columnWidth,
|
||||
columnWidths,
|
||||
isSelected,
|
||||
isEditing,
|
||||
displayValue,
|
||||
@@ -79,17 +140,63 @@ const Cell = memo(
|
||||
setActiveSheet,
|
||||
activeCellRef,
|
||||
style, // Добавляем style для react-window
|
||||
mergedCellInfo,
|
||||
}: CellProps) => {
|
||||
// Если ячейка объединена, но не является основной, не рендерим её
|
||||
if (mergedCellInfo?.isMerged && !mergedCellInfo.isMainCell) {
|
||||
return null
|
||||
}
|
||||
|
||||
const className = `border border-border ${isSelected ? 'bg-accent' : ''} ${
|
||||
isModified ? 'bg-yellow-50' : ''
|
||||
}`
|
||||
|
||||
const cellStyle: CSSProperties = {
|
||||
// Для объединенных ячеек нужно рассчитать правильные размеры
|
||||
let cellStyle: CSSProperties = {
|
||||
...style,
|
||||
overflow: 'visible',
|
||||
padding: 0,
|
||||
}
|
||||
|
||||
// Если это основная ячейка объединенной группы, расширяем её размеры
|
||||
if (
|
||||
mergedCellInfo?.isMainCell &&
|
||||
mergedCellInfo.rowSpan &&
|
||||
mergedCellInfo.colSpan
|
||||
) {
|
||||
const { rowSpan, colSpan } = mergedCellInfo
|
||||
|
||||
// Рассчитываем общую ширину для объединенной ячейки
|
||||
let totalWidth = 0
|
||||
for (let i = 0; i < colSpan; i++) {
|
||||
const currentColIndex = colIndex + i
|
||||
if (currentColIndex < columnWidths.length) {
|
||||
totalWidth += columnWidths[currentColIndex]
|
||||
}
|
||||
}
|
||||
|
||||
// Рассчитываем общую высоту для объединенной ячейки
|
||||
const totalHeight = ROW_HEIGHT * rowSpan
|
||||
|
||||
cellStyle = {
|
||||
...cellStyle,
|
||||
width: totalWidth,
|
||||
height: totalHeight,
|
||||
zIndex: 10, // Поднимаем выше обычных ячеек
|
||||
backgroundColor: isSelected
|
||||
? 'hsl(var(--accent))'
|
||||
: isModified
|
||||
? '#fefce8'
|
||||
: 'white',
|
||||
}
|
||||
}
|
||||
|
||||
// Используем значение из объединенной ячейки, если доступно
|
||||
const cellDisplayValue =
|
||||
mergedCellInfo?.isMainCell && mergedCellInfo.mergedValue !== undefined
|
||||
? String(mergedCellInfo.mergedValue)
|
||||
: displayValue
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
setActiveSheet(sheetType)
|
||||
onCellClick(rowIndex, colIndex)
|
||||
@@ -145,7 +252,7 @@ const Cell = memo(
|
||||
maxWidth: hasRightContent ? `${columnWidth - 16}px` : 'none',
|
||||
}}
|
||||
>
|
||||
{displayValue}
|
||||
{cellDisplayValue}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -271,6 +378,7 @@ const CellRenderer = ({
|
||||
isEditing,
|
||||
editingValue,
|
||||
cachedData,
|
||||
mergedCells,
|
||||
handleCellClick,
|
||||
handleCellDoubleClick,
|
||||
handleCellChange,
|
||||
@@ -293,6 +401,22 @@ const CellRenderer = ({
|
||||
? '' // Не показывать вычисленное значение во время редактирования
|
||||
: cellData?.displayValue || ''
|
||||
|
||||
// Получаем информацию об объединенных ячейках только для листа отчета
|
||||
const mergedCellInfo =
|
||||
sheetType === 'report' && mergedCells
|
||||
? findMergedCellInfo(rowIndex, columnIndex, mergedCells)
|
||||
: { isMerged: false, isMainCell: false }
|
||||
|
||||
const mergedCellProps = mergedCellInfo.isMerged
|
||||
? {
|
||||
isMerged: true,
|
||||
isMainCell: mergedCellInfo.isMainCell,
|
||||
rowSpan: mergedCellInfo.spans?.rowSpan,
|
||||
colSpan: mergedCellInfo.spans?.colSpan,
|
||||
mergedValue: mergedCellInfo.mergedCell?.value,
|
||||
}
|
||||
: undefined
|
||||
|
||||
return (
|
||||
<Cell
|
||||
style={style}
|
||||
@@ -300,6 +424,7 @@ const CellRenderer = ({
|
||||
rowIndex={rowIndex}
|
||||
colIndex={columnIndex}
|
||||
columnWidth={widths[columnIndex]}
|
||||
columnWidths={widths}
|
||||
isSelected={isCellSelected}
|
||||
isEditing={isCellEditing}
|
||||
displayValue={displayValue}
|
||||
@@ -312,6 +437,7 @@ const CellRenderer = ({
|
||||
stopEditing={stopEditing}
|
||||
setActiveSheet={setActiveSheet}
|
||||
activeCellRef={activeCellInputRef}
|
||||
mergedCellInfo={mergedCellProps}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -349,6 +475,7 @@ interface SpreadsheetProps {
|
||||
colIndex: number,
|
||||
newWidth: number,
|
||||
) => void
|
||||
mergedCells?: MergedCell[] // Добавляем поддержку объединенных ячеек
|
||||
}
|
||||
|
||||
// --- Spreadsheet Component ---
|
||||
@@ -374,6 +501,7 @@ const Spreadsheet = ({
|
||||
gridRefs,
|
||||
onGridScroll,
|
||||
handleColumnResize,
|
||||
mergedCells,
|
||||
}: SpreadsheetProps) => {
|
||||
const widths = columnWidths
|
||||
const cachedData = getCachedCellData(sheetType)
|
||||
@@ -386,6 +514,7 @@ const Spreadsheet = ({
|
||||
isEditing,
|
||||
editingValue,
|
||||
cachedData,
|
||||
mergedCells,
|
||||
handleCellClick,
|
||||
handleCellDoubleClick,
|
||||
handleCellChange,
|
||||
@@ -530,7 +659,10 @@ const Spreadsheet = ({
|
||||
)
|
||||
}
|
||||
|
||||
const DualSpreadsheet: FC<DualSpreadsheetProps> = ({ templateData = {} }) => {
|
||||
const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
|
||||
templateData = {},
|
||||
mergedCells = [],
|
||||
}) => {
|
||||
const { revision, updateRevision, ...multiSheetEngine } = useMultiSheetEngine(
|
||||
{
|
||||
sheets: [
|
||||
@@ -996,6 +1128,7 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({ templateData = {} }) => {
|
||||
gridRefs={gridRefs}
|
||||
onGridScroll={onGridScroll}
|
||||
handleColumnResize={handleColumnResize}
|
||||
mergedCells={mergedCells}
|
||||
/>
|
||||
<div
|
||||
ref={setSeparatorRef}
|
||||
@@ -1025,6 +1158,7 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({ templateData = {} }) => {
|
||||
gridRefs={gridRefs}
|
||||
onGridScroll={onGridScroll}
|
||||
handleColumnResize={handleColumnResize}
|
||||
mergedCells={mergedCells}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -240,7 +240,11 @@ export const TemplateEditor: React.FC<TemplateEditorProps> = ({
|
||||
|
||||
{/* Таблицы - теперь занимают всё оставшееся пространство */}
|
||||
<div className="min-h-0 flex-1">
|
||||
<DualSpreadsheet key={dataVersion} templateData={{ L: excelData }} />
|
||||
<DualSpreadsheet
|
||||
key={dataVersion}
|
||||
templateData={{ L: excelData }}
|
||||
mergedCells={editedTemplate.mergedCells}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user