"use client"; import { useState, useEffect, useCallback, useMemo } from "react"; import { TableRowConfig } from "@/components/ui/row-settings"; // 配置存储的键前缀 const CONFIG_STORAGE_PREFIX = "table_row_config_"; // 默认配置生成函数 function createDefaultConfig(rowIds: string[]): Record { const config: Record = {}; rowIds.forEach((rowId, index) => { config[rowId] = { rowId, isVisible: true, displayOrder: index }; }); return config; } // 检查localStorage是否可用 function isLocalStorageAvailable(): boolean { try { const testKey = '__localStorage_test__'; localStorage.setItem(testKey, 'test'); localStorage.removeItem(testKey); return true; } catch { return false; } } // 从localStorage加载配置 function loadConfigFromStorage(companyCode: string, rowIds: string[]): { config: Record; customRows: Record; } { // 检查localStorage是否可用 if (!isLocalStorageAvailable()) { console.warn('localStorage is not available, using default config'); return { config: createDefaultConfig(rowIds), customRows: {} }; } try { const storageKey = `${CONFIG_STORAGE_PREFIX}${companyCode}`; const stored = localStorage.getItem(storageKey); if (!stored) { return { config: createDefaultConfig(rowIds), customRows: {} }; } const parsed = JSON.parse(stored); // 检查配置版本兼容性 if (parsed._version && parsed._version !== '1.0') { console.warn('Incompatible config version, resetting to default'); return { config: createDefaultConfig(rowIds), customRows: {} }; } // 验证配置完整性,确保所有rowId都存在 const config: Record = {}; let hasInvalidConfig = false; rowIds.forEach((rowId, index) => { if (parsed[rowId] && typeof parsed[rowId].isVisible === 'boolean') { config[rowId] = { rowId, isVisible: parsed[rowId].isVisible, displayOrder: parsed[rowId].displayOrder ?? index }; } else { // 如果配置不存在或无效,使用默认值 config[rowId] = { rowId, isVisible: true, displayOrder: index }; hasInvalidConfig = true; } }); // 加载自定义行数据 const customRows = parsed._customRows || {}; // 将自定义行也添加到配置中 Object.entries(customRows).forEach(([rowId, customRow]) => { if (customRow && typeof customRow === 'object' && 'customRowType' in customRow && customRow.customRowType === 'separator') { config[rowId] = { rowId, isVisible: parsed[rowId]?.isVisible ?? true, displayOrder: parsed[rowId]?.displayOrder ?? Object.keys(config).length, isCustomRow: true, customRowType: 'separator' as const }; } }); // 如果有无效配置,保存修复后的配置 if (hasInvalidConfig) { saveConfigToStorage(companyCode, config, customRows); } return { config, customRows }; } catch (error) { console.warn('Failed to load row config from localStorage:', error); // 尝试清除损坏的配置 try { const storageKey = `${CONFIG_STORAGE_PREFIX}${companyCode}`; localStorage.removeItem(storageKey); } catch { // 忽略清除失败 } return { config: createDefaultConfig(rowIds), customRows: {} }; } } // 保存配置到localStorage function saveConfigToStorage( companyCode: string, config: Record, customRows?: Record ): { success: boolean; error?: string } { if (!isLocalStorageAvailable()) { const error = 'localStorage不可用,配置无法持久化保存'; console.warn(error); return { success: false, error }; } try { const storageKey = `${CONFIG_STORAGE_PREFIX}${companyCode}`; const configWithVersion = { ...config, _customRows: customRows || {}, _version: '1.0', _timestamp: Date.now() }; localStorage.setItem(storageKey, JSON.stringify(configWithVersion)); return { success: true }; } catch (error) { console.warn('Failed to save row config to localStorage:', error); // 如果是存储空间不足,尝试清理旧配置 if (error instanceof Error && error.name === 'QuotaExceededError') { try { cleanupOldConfigs(); // 重试保存 const configWithVersion = { ...config, _version: '1.0', _timestamp: Date.now() }; const storageKey = `${CONFIG_STORAGE_PREFIX}${companyCode}`; localStorage.setItem(storageKey, JSON.stringify(configWithVersion)); return { success: true }; } catch (retryError) { const errorMsg = '存储空间不足,清理后仍无法保存配置'; console.warn(errorMsg, retryError); return { success: false, error: errorMsg }; } } const errorMsg = error instanceof Error ? error.message : '配置保存失败'; return { success: false, error: errorMsg }; } } // 清理旧的配置数据 function cleanupOldConfigs(): void { try { const keysToRemove: string[] = []; const cutoffTime = Date.now() - (30 * 24 * 60 * 60 * 1000); // 30天前 for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key && key.startsWith(CONFIG_STORAGE_PREFIX)) { try { const stored = localStorage.getItem(key); if (stored) { const parsed = JSON.parse(stored); if (parsed._timestamp && parsed._timestamp < cutoffTime) { keysToRemove.push(key); } } } catch { // 如果解析失败,也标记为删除 keysToRemove.push(key); } } } keysToRemove.forEach(key => { localStorage.removeItem(key); }); console.log(`Cleaned up ${keysToRemove.length} old config entries`); } catch (error) { console.warn('Failed to cleanup old configs:', error); } } // 配置保存状态接口 export interface ConfigSaveStatus { status: 'idle' | 'saving' | 'success' | 'error'; message?: string; timestamp?: number; } // 行配置管理Hook export function useRowConfig(companyCode: string | null, rowIds: string[]) { // Create a stable reference for rowIds to prevent unnecessary re-renders const stableRowIds = useMemo(() => rowIds, [rowIds]); const [rowConfigs, setRowConfigs] = useState>(() => { // 初始化时如果有公司代码,尝试加载配置 if (companyCode && stableRowIds.length > 0) { const { config } = loadConfigFromStorage(companyCode, stableRowIds); return config; } return createDefaultConfig(stableRowIds); }); // 自定义行数据存储 const [customRows, setCustomRows] = useState>(() => { // 初始化时如果有公司代码,尝试加载自定义行数据 if (companyCode && stableRowIds.length > 0) { const { customRows } = loadConfigFromStorage(companyCode, stableRowIds); return customRows; } return {}; }); // 配置保存状态 const [saveStatus, setSaveStatus] = useState({ status: 'idle' }); // 当公司代码或行ID变化时,重新加载配置 useEffect(() => { if (companyCode && stableRowIds.length > 0) { const { config, customRows: loadedCustomRows } = loadConfigFromStorage(companyCode, stableRowIds); setRowConfigs(config); setCustomRows(loadedCustomRows); } else if (stableRowIds.length > 0) { // 没有公司代码时使用默认配置 setRowConfigs(createDefaultConfig(stableRowIds)); setCustomRows({}); } }, [companyCode, stableRowIds]); // 安全保存配置的内部函数 const saveConfigSafely = useCallback((config: Record, customRowsData?: Record) => { if (!companyCode) return; setSaveStatus({ status: 'saving' }); // 使用 setTimeout 来模拟异步保存,避免阻塞UI setTimeout(() => { const result = saveConfigToStorage(companyCode, config, customRowsData || customRows); if (result.success) { setSaveStatus({ status: 'success', message: '配置已保存', timestamp: Date.now() }); // 成功状态2秒后自动清除 setTimeout(() => { setSaveStatus(prev => prev.status === 'success' ? { status: 'idle' } : prev); }, 2000); } else { setSaveStatus({ status: 'error', message: result.error || '配置保存失败', timestamp: Date.now() }); // 错误状态5秒后自动清除 setTimeout(() => { setSaveStatus(prev => prev.status === 'error' ? { status: 'idle' } : prev); }, 5000); } }, 0); }, [companyCode, customRows]); // 更新配置 const updateRowConfig = useCallback((newConfig: Record) => { setRowConfigs(newConfig); // 如果有公司代码,保存到localStorage if (companyCode) { saveConfigSafely(newConfig, customRows); } }, [companyCode, saveConfigSafely, customRows]); // 切换单个行的可见性 const toggleRowVisibility = useCallback((rowId: string) => { setRowConfigs(prev => { const newConfig = { ...prev, [rowId]: { ...prev[rowId], isVisible: !prev[rowId]?.isVisible } }; if (companyCode) { saveConfigSafely(newConfig, customRows); } return newConfig; }); }, [companyCode, saveConfigSafely, customRows]); // 重置配置为默认值 const resetConfig = useCallback(() => { const defaultConfig = createDefaultConfig(stableRowIds); const emptyCustomRows = {}; setRowConfigs(defaultConfig); setCustomRows(emptyCustomRows); if (companyCode) { saveConfigSafely(defaultConfig, emptyCustomRows); } }, [companyCode, stableRowIds, saveConfigSafely]); // 获取可见的行ID列表 const visibleRowIds = Object.entries(rowConfigs) .filter(([, config]) => config.isVisible) .sort((a, b) => (a[1].displayOrder ?? 0) - (b[1].displayOrder ?? 0)) .map(([rowId]) => rowId); // 导出配置 const exportConfig = useCallback(() => { if (!companyCode) return null; return { companyCode, config: rowConfigs, exportedAt: new Date().toISOString(), version: '1.0' }; }, [companyCode, rowConfigs]); // 导入配置 const importConfig = useCallback((importedData: unknown) => { try { if (!importedData || typeof importedData !== 'object') { throw new Error('Invalid import data format'); } const data = importedData as Record; if (!data.config || !data.companyCode) { throw new Error('Invalid import data format'); } if (data.companyCode !== companyCode) { console.warn('Imported config is for a different company'); return false; } // 验证导入的配置 const importedConfig = data.config as Record; const validatedConfig: Record = {}; stableRowIds.forEach((rowId, index) => { const rowConfig = importedConfig[rowId] as Record | undefined; if (rowConfig && typeof rowConfig.isVisible === 'boolean') { validatedConfig[rowId] = { rowId, isVisible: rowConfig.isVisible, displayOrder: typeof rowConfig.displayOrder === 'number' ? rowConfig.displayOrder : index }; } else { validatedConfig[rowId] = { rowId, isVisible: true, displayOrder: index }; } }); setRowConfigs(validatedConfig); if (companyCode) { saveConfigSafely(validatedConfig); } return true; } catch (error) { console.warn('Failed to import config:', error); return false; } }, [companyCode, stableRowIds, saveConfigSafely]); // 获取配置统计信息 const getConfigStats = useCallback(() => { const total = Object.keys(rowConfigs).length; const visible = Object.values(rowConfigs).filter(config => config.isVisible).length; const hidden = total - visible; return { total, visible, hidden, visibilityRate: total > 0 ? Math.round((visible / total) * 100) : 0 }; }, [rowConfigs]); // 添加自定义行(仅支持分隔线) const addCustomRow = useCallback((rowType: 'separator', displayText?: string) => { // 强制只支持分隔线类型 const actualRowType = 'separator'; const rowId = `separator_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const text = displayText || '分组标题'; // 添加到自定义行数据 setCustomRows(prev => ({ ...prev, [rowId]: { displayText: text, customRowType: actualRowType } })); // 添加到行配置 const newConfig: Record = { ...rowConfigs, [rowId]: { rowId, isVisible: true, displayOrder: Object.keys(rowConfigs).length, isCustomRow: true, customRowType: actualRowType } }; setRowConfigs(newConfig); if (companyCode) { // 获取更新后的自定义行数据 const updatedCustomRows: Record = { ...customRows, [rowId]: { displayText: text, customRowType: 'separator' } }; saveConfigSafely(newConfig, updatedCustomRows); } return rowId; }, [rowConfigs, customRows, companyCode, saveConfigSafely]); // 删除自定义行 const deleteCustomRow = useCallback((rowId: string) => { if (!rowConfigs[rowId]?.isCustomRow) { console.warn('Cannot delete non-custom row:', rowId); return; } // 从自定义行数据中删除 const updatedCustomRows = { ...customRows }; delete updatedCustomRows[rowId]; setCustomRows(updatedCustomRows); // 从行配置中删除 const newConfig = { ...rowConfigs }; delete newConfig[rowId]; setRowConfigs(newConfig); if (companyCode) { saveConfigSafely(newConfig, updatedCustomRows); } }, [rowConfigs, customRows, companyCode, saveConfigSafely]); // 更新行顺序 const updateRowOrder = useCallback((newOrder: string[]) => { const newConfig = { ...rowConfigs }; newOrder.forEach((rowId, index) => { if (newConfig[rowId]) { newConfig[rowId] = { ...newConfig[rowId], displayOrder: index }; } }); setRowConfigs(newConfig); if (companyCode) { saveConfigSafely(newConfig, customRows); } }, [rowConfigs, customRows, companyCode, saveConfigSafely]); // 清除保存状态 const clearSaveStatus = useCallback(() => { setSaveStatus({ status: 'idle' }); }, []); return { rowConfigs, customRows, updateRowConfig, toggleRowVisibility, resetConfig, visibleRowIds, exportConfig, importConfig, getConfigStats, saveStatus, clearSaveStatus, addCustomRow, deleteCustomRow, updateRowOrder }; }