refactor(frontend): UI/UX改进和组件优化
- DataTable组件操作列自适应 - 优化各种Modal弹窗 - 统一API调用方式(AbortSignal) - 添加全局订阅状态管理 - 优化各管理视图的交互和布局 - 修复国际化翻译问题
This commit is contained in:
135
frontend/src/stores/subscriptions.ts
Normal file
135
frontend/src/stores/subscriptions.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Subscription Store
|
||||
* Global state management for user subscriptions with caching and deduplication
|
||||
*/
|
||||
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import subscriptionsAPI from '@/api/subscriptions'
|
||||
import type { UserSubscription } from '@/types'
|
||||
|
||||
// Cache TTL: 60 seconds
|
||||
const CACHE_TTL_MS = 60_000
|
||||
|
||||
// Request generation counter to invalidate stale in-flight responses
|
||||
let requestGeneration = 0
|
||||
|
||||
export const useSubscriptionStore = defineStore('subscriptions', () => {
|
||||
// State
|
||||
const activeSubscriptions = ref<UserSubscription[]>([])
|
||||
const loading = ref(false)
|
||||
const loaded = ref(false)
|
||||
const lastFetchedAt = ref<number | null>(null)
|
||||
|
||||
// In-flight request deduplication
|
||||
let activePromise: Promise<UserSubscription[]> | null = null
|
||||
|
||||
// Auto-refresh interval
|
||||
let pollerInterval: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
// Computed
|
||||
const hasActiveSubscriptions = computed(() => activeSubscriptions.value.length > 0)
|
||||
|
||||
/**
|
||||
* Fetch active subscriptions with caching and deduplication
|
||||
* @param force - Force refresh even if cache is valid
|
||||
*/
|
||||
async function fetchActiveSubscriptions(force = false): Promise<UserSubscription[]> {
|
||||
const now = Date.now()
|
||||
|
||||
// Return cached data if valid
|
||||
if (
|
||||
!force &&
|
||||
loaded.value &&
|
||||
lastFetchedAt.value &&
|
||||
now - lastFetchedAt.value < CACHE_TTL_MS
|
||||
) {
|
||||
return activeSubscriptions.value
|
||||
}
|
||||
|
||||
// Return in-flight request if exists (deduplication)
|
||||
if (activePromise) {
|
||||
return activePromise
|
||||
}
|
||||
|
||||
const currentGeneration = ++requestGeneration
|
||||
|
||||
// Start new request
|
||||
loading.value = true
|
||||
activePromise = subscriptionsAPI
|
||||
.getActiveSubscriptions()
|
||||
.then((data) => {
|
||||
if (currentGeneration === requestGeneration) {
|
||||
activeSubscriptions.value = data
|
||||
loaded.value = true
|
||||
lastFetchedAt.value = Date.now()
|
||||
}
|
||||
return data
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to fetch active subscriptions:', error)
|
||||
throw error
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
activePromise = null
|
||||
})
|
||||
|
||||
return activePromise
|
||||
}
|
||||
|
||||
/**
|
||||
* Start auto-refresh polling (every 5 minutes)
|
||||
*/
|
||||
function startPolling() {
|
||||
if (pollerInterval) return
|
||||
|
||||
pollerInterval = setInterval(() => {
|
||||
fetchActiveSubscriptions(true).catch((error) => {
|
||||
console.error('Subscription polling failed:', error)
|
||||
})
|
||||
}, 5 * 60 * 1000) // 5 minutes
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop auto-refresh polling
|
||||
*/
|
||||
function stopPolling() {
|
||||
if (pollerInterval) {
|
||||
clearInterval(pollerInterval)
|
||||
pollerInterval = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all subscription data and stop polling
|
||||
*/
|
||||
function clear() {
|
||||
requestGeneration++
|
||||
activeSubscriptions.value = []
|
||||
loaded.value = false
|
||||
lastFetchedAt.value = null
|
||||
stopPolling()
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate cache (force next fetch to reload)
|
||||
*/
|
||||
function invalidateCache() {
|
||||
lastFetchedAt.value = null
|
||||
}
|
||||
|
||||
return {
|
||||
// State
|
||||
activeSubscriptions,
|
||||
loading,
|
||||
hasActiveSubscriptions,
|
||||
|
||||
// Actions
|
||||
fetchActiveSubscriptions,
|
||||
startPolling,
|
||||
stopPolling,
|
||||
clear,
|
||||
invalidateCache
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user