удаление неиспользуемого

This commit is contained in:
2025-07-30 18:18:19 +03:00
parent a7780279e0
commit d692689961
13 changed files with 17 additions and 659 deletions

View File

@@ -1,10 +1,5 @@
import {
getFileData,
getFilesByTemplateId,
getLatestFileForTemplate,
parseExcelFile,
} from '@/service/fileApiService'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { getFileData, getLatestFileForTemplate } from '@/service/fileApiService'
import { useQuery } from '@tanstack/react-query'
// Ключи для React Query
export const fileKeys = {
@@ -16,17 +11,6 @@ export const fileKeys = {
data: (fileId: string) => [...fileKeys.all, 'data', fileId] as const,
}
// Хук для получения файлов по template_id с кешированием
export function useFilesByTemplate(templateId: string | undefined) {
return useQuery({
queryKey: fileKeys.byTemplate(templateId || ''),
queryFn: () => getFilesByTemplateId(templateId!),
enabled: !!templateId,
staleTime: 5 * 60 * 1000, // 5 минут
gcTime: 10 * 60 * 1000, // 10 минут кеш
})
}
// Хук для получения последнего файла для шаблона
export function useLatestFileForTemplate(templateId: string | undefined) {
return useQuery({
@@ -48,38 +32,3 @@ export function useFileData(fileId: string | undefined) {
gcTime: 30 * 60 * 1000, // 30 минут кеш
})
}
// Хук для загрузки Excel файла
export function useParseExcelFile() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: ({ file, templateId }: { file: File; templateId: string }) =>
parseExcelFile(file, templateId),
onSuccess: (data, variables) => {
// Инвалидируем связанные кеши после успешной загрузки
queryClient.invalidateQueries({
queryKey: fileKeys.byTemplate(variables.templateId),
})
queryClient.invalidateQueries({
queryKey: fileKeys.latest(variables.templateId),
})
// Сохраняем данные файла в кеш
queryClient.setQueryData(fileKeys.data(data.fileId), data)
},
})
}
// Хук для предзагрузки данных файла
export function usePrefetchFileData() {
const queryClient = useQueryClient()
return (fileId: string) => {
queryClient.prefetchQuery({
queryKey: fileKeys.data(fileId),
queryFn: () => getFileData(fileId),
staleTime: 10 * 60 * 1000,
})
}
}

View File

@@ -69,14 +69,7 @@ export function useMultiSheetEngine({
if (!workbookRef.current) return
// Предотвращаем случайную очистку данных при смене вкладки
if (document.hidden) {
if (process.env.NODE_ENV !== 'production') {
console.log(
'⚠️ useMultiSheetEngine: предотвращаем загрузку данных когда страница скрыта'
)
}
return
}
if (document.hidden) return
// Создаем ключ для проверки изменений данных
const dataKey = JSON.stringify(initialData)
@@ -85,21 +78,10 @@ export function useMultiSheetEngine({
if (
loadedDataRef.current === dataKey &&
Object.keys(initialData).length > 0
) {
if (process.env.NODE_ENV !== 'production') {
console.log('⏭️ useMultiSheetEngine: данные уже загружены, пропускаем')
}
)
return
}
if (Object.keys(initialData).length > 0) {
if (process.env.NODE_ENV !== 'production') {
console.log(
'🔄 useMultiSheetEngine: загрузка initialData:',
Object.keys(initialData)
)
}
// Очищаем только те листы, для которых есть новые данные
for (const sheetName of Object.keys(initialData)) {
const sheet = workbookRef.current.getSheet(sheetName)
@@ -117,42 +99,6 @@ export function useMultiSheetEngine({
}
}, [initialData, safeRecalc])
// Синхронизация всех листов из движка
// const syncAllSheetsFromEngine = useCallback(() => {
// if (!workbookRef.current || isRecalculatingRef.current) return
// const newSheetCells: Record<string, CellData[][]> = {}
// sheets.forEach((sheetConfig) => {
// const sheet = workbookRef.current!.getSheet(sheetConfig.name)
// if (!sheet) return
// const cells = Array(sheetConfig.rows)
// .fill(null)
// .map(() =>
// Array(sheetConfig.cols)
// .fill(null)
// .map(() => ({ value: '', isSelected: false })),
// )
// // Получаем все ячейки из движка
// for (let row = 0; row < sheetConfig.rows; row++) {
// for (let col = 0; col < sheetConfig.cols; col++) {
// const cellName = columnToLabel(col) + (row + 1)
// const cell = sheet.getCell(cellName)
// if (cell.raw !== null && cell.raw !== undefined) {
// cells[row][col] = String(cell.raw)
// }
// }
// }
// newSheetCells[sheetConfig.name] = cells
// })
// // setSheetCells(newSheetCells) // Удалено, так как sheetCells убрано
// }, [sheets])
// Отложенный пересчет с дебаунсом
const debouncedRecalc = useCallback(() => {
if (!workbookRef.current || isRecalculatingRef.current) return
@@ -276,15 +222,6 @@ export function useMultiSheetEngine({
const setTemplateData = useCallback(
(data: Record<string, Record<string, any>>) => {
templateDataRef.current = JSON.parse(JSON.stringify(data)) // Глубокая копия
if (process.env.NODE_ENV !== 'production') {
console.log('📋 Установлены базовые данные шаблона:', {
sheets: Object.keys(templateDataRef.current),
cellCount: Object.values(templateDataRef.current).reduce(
(total, sheet) => total + Object.keys(sheet).length,
0
),
})
}
},
[]
)
@@ -333,16 +270,6 @@ export function useMultiSheetEngine({
if (normalizedCurrent !== normalizedTemplate) {
// Если текущего значения нет (ячейка очищена), отправляем ''
modifiedSheet[cellAddress] = currentVal
if (process.env.NODE_ENV !== 'production') {
console.log(
`🔧 Пользовательское изменение ${sheetName}:${cellAddress}:`,
{
template: normalizedTemplate,
current: normalizedCurrent,
value: currentVal,
}
)
}
}
})
@@ -351,22 +278,6 @@ export function useMultiSheetEngine({
}
})
if (process.env.NODE_ENV !== 'production') {
const totalModified = Object.values(modifiedData).reduce(
(total, sheet) => total + Object.keys(sheet).length,
0
)
console.log(`📊 Найдено ${totalModified} измененных ячеек:`, {
sheets: Object.keys(modifiedData),
details: Object.fromEntries(
Object.entries(modifiedData).map(([sheetName, cells]) => [
sheetName,
Object.keys(cells).length,
])
),
})
}
return modifiedData
}, [])
@@ -405,7 +316,6 @@ export function useMultiSheetEngine({
}, [])
return {
// sheetCells убран
setCellValue,
setCellValueWithoutRecalc,
debouncedRecalc,

View File

@@ -1,7 +1,5 @@
import {
createSheet,
getSheet,
getSheets,
getSheetsByTemplate,
patchSheet,
type ApiSheet,
@@ -18,16 +16,6 @@ export const sheetKeys = {
detail: (sheetId: string) => [...sheetKeys.all, 'detail', sheetId] as const,
}
// Хук для получения всех листов
export function useSheets() {
return useQuery({
queryKey: sheetKeys.all,
queryFn: getSheets,
staleTime: 2 * 60 * 1000, // 2 минуты
gcTime: 10 * 60 * 1000, // 10 минут кеш
})
}
// Хук для получения листов по template_id с кешированием
export function useSheetsByTemplate(templateId: string | undefined) {
return useQuery({
@@ -40,17 +28,6 @@ export function useSheetsByTemplate(templateId: string | undefined) {
})
}
// Хук для получения конкретного листа
export function useSheet(sheetId: string | undefined) {
return useQuery({
queryKey: sheetKeys.detail(sheetId || ''),
queryFn: () => getSheet(sheetId!),
enabled: !!sheetId,
staleTime: 5 * 60 * 1000, // 5 минут
gcTime: 10 * 60 * 1000, // 10 минут кеш
})
}
// Хук для создания листа
export function useCreateSheet() {
const queryClient = useQueryClient()
@@ -149,48 +126,3 @@ export function usePatchSheet() {
},
})
}
// Хук для массового обновления листов (для автосохранения)
export function useBatchUpdateSheets() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (
updates: Array<{
sheetId: string
data: PatchSheetInput
templateId: string
}>
) => {
// Выполняем все обновления параллельно
const results = await Promise.allSettled(
updates.map(({ sheetId, data }) => patchSheet(sheetId, data))
)
return results
},
onSuccess: (results, variables) => {
// Обновляем кеши для всех затронутых шаблонов
const templateIds = new Set(variables.map(v => v.templateId))
templateIds.forEach(templateId => {
queryClient.invalidateQueries({
queryKey: sheetKeys.byTemplate(templateId),
})
})
},
})
}
// Хук для предзагрузки листов шаблона
export function usePrefetchSheetsByTemplate() {
const queryClient = useQueryClient()
return (templateId: string) => {
queryClient.prefetchQuery({
queryKey: sheetKeys.byTemplate(templateId),
queryFn: () => getSheetsByTemplate(templateId),
staleTime: 1 * 60 * 1000,
})
}
}

View File

@@ -1,335 +0,0 @@
import { useCallback, useEffect, useRef, useState } from 'react'
import {
CellUtils,
CellValue,
Workbook,
} from '../feature/excelEngine/excelEngine'
interface CellData {
value: string
isSelected: boolean
}
interface UseSpreadsheetEngineProps {
rows?: number
cols?: number
sheetName?: string
initialData?: string[][]
}
export function useSpreadsheetEngine({
rows = 50,
cols = 20,
sheetName = 'Sheet1',
initialData,
}: UseSpreadsheetEngineProps = {}) {
const workbookRef = useRef<Workbook | null>(null)
const [cells, setCells] = useState<CellData[][]>(() => {
return Array(rows)
.fill(null)
.map(() =>
Array(cols)
.fill(null)
.map(() => ({ value: '', isSelected: false }))
)
})
const [isCalculating, setIsCalculating] = useState(false)
// Инициализация workbook
useEffect(() => {
if (!workbookRef.current) {
workbookRef.current = new Workbook()
workbookRef.current.addSheet(sheetName)
// addCustomFunctions(workbookRef.current)
}
// Загружаем начальные данные если они есть
if (initialData) {
loadDataFromArray(initialData)
}
}, [sheetName, initialData])
// Конвертация данных из движка в React состояние
const syncFromEngine = useCallback(() => {
if (!workbookRef.current) return
const sheet = workbookRef.current.getSheet(sheetName)
if (!sheet) return
const newCells = Array(rows)
.fill(null)
.map(() =>
Array(cols)
.fill(null)
.map(() => ({ value: '', isSelected: false }))
)
// Получаем все ячейки из движка
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
const cellName = CellUtils.createCellName(col, row + 1)
const cell = sheet.getCell(cellName)
// Показываем сырое значение (raw) в UI, а не вычисленное
if (cell.raw !== null && cell.raw !== undefined) {
newCells[row][col].value = String(cell.raw)
}
}
}
setCells(newCells)
}, [rows, cols, sheetName])
// Установка значения ячейки
const setCellValue = useCallback(
(row: number, col: number, value: string) => {
if (!workbookRef.current) return
const cellName = CellUtils.createCellName(col, row + 1)
const qualifiedName = `${sheetName}!${cellName}`
setIsCalculating(true)
try {
// Конвертируем пустую строку в null
const cellValue: CellValue = value === '' ? null : value
// Если значение начинается с =, то это формула
if (typeof cellValue === 'string' && cellValue.startsWith('=')) {
workbookRef.current.setCell(qualifiedName, cellValue)
} else {
// Пытаемся конвертировать в число, если возможно
const numValue = Number(cellValue)
if (!isNaN(numValue) && cellValue !== null && cellValue !== '') {
workbookRef.current.setCell(qualifiedName, numValue)
} else {
workbookRef.current.setCell(qualifiedName, cellValue)
}
}
// Пересчитываем все формулы
workbookRef.current.recalc()
// Синхронизируем с React состоянием
syncFromEngine()
} catch (error) {
console.error('Error setting cell value:', error)
} finally {
setIsCalculating(false)
}
},
[sheetName, syncFromEngine]
)
// Получение вычисленного значения ячейки
const getCellValue = useCallback(
(row: number, col: number): CellValue => {
if (!workbookRef.current) return null
const cellName = CellUtils.createCellName(col, row + 1)
const qualifiedName = `${sheetName}!${cellName}`
try {
return workbookRef.current.getValue(qualifiedName)
} catch (error) {
console.error('Error getting cell value:', error)
return '#ERROR!'
}
},
[sheetName]
)
// Получение сырого значения ячейки (для отображения в строке формул)
const getCellRawValue = useCallback(
(row: number, col: number): string => {
if (!workbookRef.current) return ''
const cellName = CellUtils.createCellName(col, row + 1)
const sheet = workbookRef.current.getSheet(sheetName)
if (!sheet) return ''
const cell = sheet.getCell(cellName)
return cell.raw !== null && cell.raw !== undefined ? String(cell.raw) : ''
},
[sheetName]
)
// Загрузка данных из массива
const loadDataFromArray = useCallback(
(data: string[][]) => {
if (!workbookRef.current) return
const sheet = workbookRef.current.getSheet(sheetName)
if (!sheet) return
setIsCalculating(true)
try {
// Очищаем лист
sheet.cells.clear()
// Загружаем данные
for (let row = 0; row < data.length && row < rows; row++) {
for (let col = 0; col < data[row].length && col < cols; col++) {
const value = data[row][col]
if (value !== null && value !== undefined && value !== '') {
const cellName = CellUtils.createCellName(col, row + 1)
sheet.setCell(cellName, value)
}
}
}
// Пересчитываем все формулы
workbookRef.current.recalc()
// Синхронизируем с React состоянием
syncFromEngine()
} catch (error) {
console.error('Error loading data:', error)
} finally {
setIsCalculating(false)
}
},
[rows, cols, sheetName, syncFromEngine]
)
// Экспорт данных в массив (для сохранения)
const exportToArray = useCallback((): string[][] => {
if (!workbookRef.current) return []
const sheet = workbookRef.current.getSheet(sheetName)
if (!sheet) return []
const result: string[][] = Array(rows)
.fill(null)
.map(() => Array(cols).fill(''))
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
const cellName = CellUtils.createCellName(col, row + 1)
const cell = sheet.getCell(cellName)
if (cell.raw !== null && cell.raw !== undefined) {
result[row][col] = String(cell.raw)
}
}
}
return result
}, [rows, cols, sheetName])
// Экспорт в JSON для бэкенда
const exportToJSON = useCallback((): string => {
if (!workbookRef.current) return '{}'
return JSON.stringify(workbookRef.current.toJSON())
}, [])
// Импорт из JSON с бэкенда
const importFromJSON = useCallback(
(jsonData: string) => {
if (!workbookRef.current) return
setIsCalculating(true)
try {
const data = JSON.parse(jsonData)
workbookRef.current.fromJSON(data)
// addCustomFunctions(workbookRef.current)
workbookRef.current.recalc()
syncFromEngine()
} catch (error) {
console.error('Error importing from JSON:', error)
} finally {
setIsCalculating(false)
}
},
[syncFromEngine]
)
// Добавление пользовательской функции
const addFunction = useCallback(
(name: string, func: (...args: any[]) => any) => {
if (!workbookRef.current) return
workbookRef.current.addFunction(name, func)
},
[]
)
// Пересчёт всех формул
const recalculate = useCallback(() => {
if (!workbookRef.current) return
setIsCalculating(true)
try {
workbookRef.current.recalc()
syncFromEngine()
} catch (error) {
console.error('Error recalculating:', error)
} finally {
setIsCalculating(false)
}
}, [syncFromEngine])
// Получение информации о зависимостях ячейки
const getCellDependencies = useCallback(
(row: number, col: number) => {
if (!workbookRef.current) return { precedents: [], dependents: [] }
const cellName = CellUtils.createCellName(col, row + 1)
const sheet = workbookRef.current.getSheet(sheetName)
if (!sheet) return { precedents: [], dependents: [] }
const cell = sheet.getCell(cellName)
return {
precedents: Array.from(cell.precedents),
dependents: Array.from(cell.dependents),
}
},
[sheetName]
)
// Получение статистики workbook
const getWorkbookStats = useCallback(() => {
if (!workbookRef.current) return { sheets: 0, cells: 0, formulas: 0 }
let totalCells = 0
let totalFormulas = 0
for (const sheet of workbookRef.current.sheets.values()) {
totalCells += sheet.cells.size
for (const cell of sheet.cells.values()) {
if (typeof cell.raw === 'string' && cell.raw.startsWith('=')) {
totalFormulas++
}
}
}
return {
sheets: workbookRef.current.sheets.size,
cells: totalCells,
formulas: totalFormulas,
}
}, [])
return {
cells,
setCellValue,
getCellValue,
getCellRawValue,
loadDataFromArray,
exportToArray,
exportToJSON,
importFromJSON,
addFunction,
recalculate,
getCellDependencies,
getWorkbookStats,
isCalculating,
workbook: workbookRef.current,
}
}