import { defineStore } from 'pinia' import { ref, computed } from 'vue' export interface OfflineAction { id: string type: 'create' | 'update' | 'delete' entity: 'transaction' | 'category' | 'account' data: any timestamp: string retryCount: number maxRetries: number error?: string } export const useOfflineStore = defineStore('offline', () => { // 网络状态 const isOnline = ref(navigator.onLine) const connectionType = ref('unknown') const lastOnlineTime = ref(null) const lastOfflineTime = ref(null) // 离线操作队列 const pendingActions = ref([]) const failedActions = ref([]) // 同步状态 const isSyncing = ref(false) const syncProgress = ref(0) const syncError = ref(null) const lastSyncTime = ref(null) // 离线提示 const showOfflineIndicator = ref(false) const offlineMessage = ref('') // 计算属性 const hasConnection = computed(() => isOnline.value) const hasPendingActions = computed(() => pendingActions.value.length > 0) const hasFailedActions = computed(() => failedActions.value.length > 0) const totalPendingCount = computed(() => pendingActions.value.length + failedActions.value.length) const connectionStatus = computed(() => { if (isOnline.value) { return { status: 'online', message: '已连接', color: 'green' } } else { return { status: 'offline', message: '离线模式', color: 'orange' } } }) // 网络状态监听 function setupNetworkListeners() { // 监听网络状态变化 window.addEventListener('online', handleOnline) window.addEventListener('offline', handleOffline) // 检测连接类型(如果支持) if ('connection' in navigator) { const connection = (navigator as any).connection connectionType.value = connection.effectiveType || 'unknown' connection.addEventListener('change', () => { connectionType.value = connection.effectiveType || 'unknown' }) } // 初始状态 updateNetworkStatus() } // 处理上线事件 function handleOnline() { isOnline.value = true lastOnlineTime.value = new Date() showOfflineIndicator.value = false console.log('网络已连接,开始同步离线数据') // 自动同步离线操作 if (hasPendingActions.value || hasFailedActions.value) { syncOfflineActions() } } // 处理离线事件 function handleOffline() { isOnline.value = false lastOfflineTime.value = new Date() showOfflineIndicator.value = true offlineMessage.value = '当前处于离线模式,数据将在网络恢复后自动同步' console.log('网络已断开,进入离线模式') } // 更新网络状态 function updateNetworkStatus() { const wasOnline = isOnline.value isOnline.value = navigator.onLine if (wasOnline !== isOnline.value) { if (isOnline.value) { handleOnline() } else { handleOffline() } } } // 添加离线操作到队列 function addOfflineAction(action: Omit) { const offlineAction: OfflineAction = { ...action, id: generateActionId(), timestamp: new Date().toISOString(), retryCount: 0 } pendingActions.value.push(offlineAction) console.log('添加离线操作到队列:', offlineAction) } // 生成操作ID function generateActionId(): string { return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}` } // 同步离线操作 async function syncOfflineActions() { if (isSyncing.value || !isOnline.value) return isSyncing.value = true syncProgress.value = 0 syncError.value = null try { const allActions = [...pendingActions.value, ...failedActions.value] const totalActions = allActions.length if (totalActions === 0) { isSyncing.value = false return } console.log(`开始同步 ${totalActions} 个离线操作`) for (let i = 0; i < allActions.length; i++) { const action = allActions[i] try { await executeOfflineAction(action) // 从队列中移除成功的操作 removeActionFromQueues(action.id) syncProgress.value = ((i + 1) / totalActions) * 100 } catch (error) { console.error('同步操作失败:', action, error) // 增加重试次数 action.retryCount++ action.error = error instanceof Error ? error.message : 'Unknown error' // 如果超过最大重试次数,移到失败队列 if (action.retryCount >= action.maxRetries) { moveActionToFailed(action) } } } lastSyncTime.value = new Date() console.log('离线操作同步完成') } catch (error) { syncError.value = error instanceof Error ? error.message : 'Sync failed' console.error('同步过程中发生错误:', error) } finally { isSyncing.value = false syncProgress.value = 100 } } // 执行离线操作 async function executeOfflineAction(action: OfflineAction) { // 这里将在实际的云端同步功能中实现 // 目前只是模拟执行 console.log('执行离线操作:', action) // 模拟网络请求延迟 await new Promise(resolve => setTimeout(resolve, 500)) // 模拟可能的失败 if (Math.random() < 0.1) { // 10% 失败率 throw new Error('模拟网络错误') } } // 从队列中移除操作 function removeActionFromQueues(actionId: string) { pendingActions.value = pendingActions.value.filter(a => a.id !== actionId) failedActions.value = failedActions.value.filter(a => a.id !== actionId) } // 将操作移到失败队列 function moveActionToFailed(action: OfflineAction) { pendingActions.value = pendingActions.value.filter(a => a.id !== action.id) if (!failedActions.value.find(a => a.id === action.id)) { failedActions.value.push(action) } } // 重试失败的操作 async function retryFailedActions() { if (!isOnline.value) { throw new Error('网络未连接,无法重试') } // 将失败的操作移回待处理队列 const actionsToRetry = failedActions.value.map(action => ({ ...action, retryCount: 0, error: undefined })) failedActions.value = [] pendingActions.value.push(...actionsToRetry) // 开始同步 await syncOfflineActions() } // 清除失败的操作 function clearFailedActions() { failedActions.value = [] } // 清除所有离线操作 function clearAllActions() { pendingActions.value = [] failedActions.value = [] } // 手动触发同步 async function manualSync() { if (!isOnline.value) { throw new Error('网络未连接,无法同步') } await syncOfflineActions() } // 获取离线统计 function getOfflineStats() { return { isOnline: isOnline.value, connectionType: connectionType.value, pendingCount: pendingActions.value.length, failedCount: failedActions.value.length, totalCount: totalPendingCount.value, lastOnlineTime: lastOnlineTime.value, lastOfflineTime: lastOfflineTime.value, lastSyncTime: lastSyncTime.value, isSyncing: isSyncing.value, syncProgress: syncProgress.value } } // 设置离线提示 function setOfflineMessage(message: string, show = true) { offlineMessage.value = message showOfflineIndicator.value = show } // 隐藏离线提示 function hideOfflineIndicator() { showOfflineIndicator.value = false } // 检查网络连接质量 async function checkConnectionQuality() { if (!isOnline.value) return 'offline' try { const start = Date.now() const response = await fetch('/api/ping', { method: 'HEAD', cache: 'no-cache' }) const duration = Date.now() - start if (response.ok) { if (duration < 100) return 'excellent' if (duration < 300) return 'good' if (duration < 1000) return 'fair' return 'poor' } else { return 'poor' } } catch (error) { return 'offline' } } // 清理资源 function cleanup() { window.removeEventListener('online', handleOnline) window.removeEventListener('offline', handleOffline) } return { // 状态 isOnline, connectionType, lastOnlineTime, lastOfflineTime, pendingActions, failedActions, isSyncing, syncProgress, syncError, lastSyncTime, showOfflineIndicator, offlineMessage, // 计算属性 hasConnection, hasPendingActions, hasFailedActions, totalPendingCount, connectionStatus, // 方法 setupNetworkListeners, updateNetworkStatus, addOfflineAction, syncOfflineActions, retryFailedActions, clearFailedActions, clearAllActions, manualSync, getOfflineStats, setOfflineMessage, hideOfflineIndicator, checkConnectionQuality, cleanup } })