refactor: replace useBackgroundOptimization with unified useBackground composable and update Nginx SSE route configuration

This commit is contained in:
jxxghp
2026-05-17 08:32:17 +08:00
parent bbe3368c69
commit 75862c026a
20 changed files with 328 additions and 147 deletions

View File

@@ -9,14 +9,14 @@ import { cloneDeep } from 'lodash-es'
import { useI18n } from 'vue-i18n'
import { downloaderDict, storageAttributes } from '@/api/constants'
import { useDisplay } from 'vuetify'
import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization'
import { useBackground } from '@/composables/useBackground'
// 显示器宽度
const display = useDisplay()
// 获取i18n实例
const { t } = useI18n()
const { useConditionalDataRefresh } = useBackgroundOptimization()
const { useConditionalDataRefresh } = useBackground()
// 定义输入
const props = defineProps({

View File

@@ -14,7 +14,7 @@ import {
TransferDirectoryConf,
TransferForm,
} from '@/api/types'
import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization'
import { useBackground } from '@/composables/useBackground'
import MediaIdSelector from '../misc/MediaIdSelector.vue'
import ProgressDialog from './ProgressDialog.vue'
import { useI18n } from 'vue-i18n'
@@ -24,7 +24,7 @@ import { useGlobalSettingsStore } from '@/stores'
// 国际化
const { t } = useI18n()
const { useProgressSSE } = useBackgroundOptimization()
const { useProgressSSE } = useBackground()
// 显示器宽度
const display = useDisplay()

View File

@@ -5,7 +5,7 @@ import api from '@/api'
import { FileItem, TransferQueue } from '@/api/types'
import { useDisplay } from 'vuetify'
import { useI18n } from 'vue-i18n'
import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization'
import { useBackground } from '@/composables/useBackground'
import CryptoJS from 'crypto-js'
type TransferTask = TransferQueue['tasks'][number]
@@ -20,7 +20,7 @@ interface MediaTaskGroup {
// 多语言支持
const { t } = useI18n()
const { useProgressSSE } = useBackgroundOptimization()
const { useProgressSSE } = useBackground()
// 显示器宽度
const display = useDisplay()

View File

@@ -11,14 +11,14 @@ import ProgressDialog from '../dialog/ProgressDialog.vue'
import { useDisplay } from 'vuetify'
import MediaInfoDialog from '../dialog/MediaInfoDialog.vue'
import { useI18n } from 'vue-i18n'
import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization'
import { useBackground } from '@/composables/useBackground'
import { usePWA } from '@/composables/usePWA'
import { useAvailableHeight } from '@/composables/useAvailableHeight'
import { useKeepAliveRefresh } from '@/composables/useKeepAliveRefresh'
// 国际化
const { t } = useI18n()
const { useProgressSSE } = useBackgroundOptimization()
const { useProgressSSE } = useBackground()
// 显示器宽度
const display = useDisplay()
@@ -652,7 +652,7 @@ function handleProgressMessage(event: MessageEvent) {
}
}
// 使用优化的进度SSE连接
// 使用进度SSE连接
const progressSSE = useProgressSSE(
`${import.meta.env.VITE_API_BASE_URL}system/progress/batchrename`,
handleProgressMessage,

View File

@@ -1,14 +1,18 @@
import { onMounted, onUnmounted, ref, type Ref } from 'vue'
import { sseManagerSingleton } from '@/utils/sseManager'
import { getCurrentInstance, onMounted, onUnmounted, ref, type Ref } from 'vue'
import { sseManagerSingleton, type SSEManagerOptions } from '@/utils/sseManager'
import { addBackgroundTimer, removeBackgroundTimer } from '@/utils/backgroundManager'
type UseSSEOptions = Partial<SSEManagerOptions> & {
connectDelay?: number
}
/**
*
* SSE连接和定时器iOS后台性能
*
* SSE连接和定时器
*/
export function useBackgroundOptimization() {
export function useBackground() {
/**
* 使SSE连接
* 使SSE连接
* @param url SSE连接地址
* @param messageHandler
* @param listenerId ID
@@ -18,24 +22,30 @@ export function useBackgroundOptimization() {
url: string,
messageHandler: (event: MessageEvent) => void,
listenerId: string,
options?: {
backgroundCloseDelay?: number
reconnectDelay?: number
maxReconnectAttempts?: number
connectDelay?: number // 新增:连接延迟
},
options?: UseSSEOptions,
) => {
// 使用独立的SSE管理器确保每个监听器都有独立的连接
const manager = sseManagerSingleton.getIndependentManager(url, listenerId, options)
const isConnected = ref(false)
let connectTimer: ReturnType<typeof setTimeout> | null = null
let isClosed = false
const statusListenerId = `${listenerId}:status`
manager.addStatusListener(statusListenerId, status => {
isConnected.value = status === 'open'
})
const cleanup = () => {
if (isClosed) return
isClosed = true
if (connectTimer) {
clearTimeout(connectTimer)
connectTimer = null
}
manager.removeStatusListener(statusListenerId)
manager.removeMessageListener(listenerId)
sseManagerSingleton.closeIndependentManager(url, listenerId)
isConnected.value = false
@@ -46,11 +56,10 @@ export function useBackgroundOptimization() {
const connectDelay = options?.connectDelay || 100
connectTimer = setTimeout(() => {
connectTimer = null
if (isClosed) return
try {
manager.addMessageListener(listenerId, event => {
messageHandler(event)
isConnected.value = true
})
manager.addMessageListener(listenerId, messageHandler)
} catch (error) {
console.error('SSE连接建立失败:', error)
}
@@ -69,7 +78,7 @@ export function useBackgroundOptimization() {
}
/**
* 使
* 使
* @param id ID
* @param callback
* @param interval
@@ -110,25 +119,40 @@ export function useBackgroundOptimization() {
messageHandler: (event: MessageEvent) => void,
listenerId: string,
delay: number = 3000,
options?: Parameters<typeof useSSE>[3],
options?: UseSSEOptions,
) => {
// 使用独立的SSE管理器确保每个监听器都有独立的连接
const manager = sseManagerSingleton.getIndependentManager(url, listenerId, options)
const isConnected = ref(false)
let connectTimer: ReturnType<typeof setTimeout> | null = null
let isClosed = false
const statusListenerId = `${listenerId}:status`
manager.addStatusListener(statusListenerId, status => {
isConnected.value = status === 'open'
})
const cleanup = () => {
if (isClosed) return
isClosed = true
if (connectTimer) {
clearTimeout(connectTimer)
connectTimer = null
}
manager.removeStatusListener(statusListenerId)
manager.removeMessageListener(listenerId)
sseManagerSingleton.closeIndependentManager(url, listenerId)
isConnected.value = false
}
onMounted(() => {
connectTimer = setTimeout(() => {
connectTimer = null
if (isClosed) return
manager.addMessageListener(listenerId, messageHandler)
}, delay)
})
@@ -139,6 +163,7 @@ export function useBackgroundOptimization() {
manager,
readyState: () => manager.readyState,
close: cleanup,
isConnected,
}
}
@@ -189,9 +214,12 @@ export function useBackgroundOptimization() {
isListening = false
}
onUnmounted(() => {
stopProgress(true)
})
// 进度监听有些场景会在用户操作后动态创建;只有 setup 阶段创建时才注册自动卸载钩子。
if (getCurrentInstance()) {
onUnmounted(() => {
stopProgress(true)
})
}
return {
start: startProgress,

View File

@@ -2,10 +2,10 @@
import { formatDateDifference } from '@core/utils/formatters'
import { SystemNotification } from '@/api/types'
import { useI18n } from 'vue-i18n'
import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization'
import { useBackground } from '@/composables/useBackground'
const { t } = useI18n()
const { useDelayedSSE } = useBackgroundOptimization()
const { useDelayedSSE } = useBackground()
// 是否有新消息
const hasNewMessage = ref(false)
@@ -39,7 +39,7 @@ function handleMessage(event: MessageEvent) {
}
}
// 使用优化的SSE连接延迟3秒启动避免认证问题
// 延迟3秒启动SSE连接避免认证信息尚未准备好。
useDelayedSSE(
`${import.meta.env.VITE_API_BASE_URL}system/message`,
handleMessage,

View File

@@ -240,6 +240,8 @@ const progressEnabled = ref(false)
// 进度是否激活
const progressActive = ref(false)
let progressResetTimer: ReturnType<typeof setTimeout> | null = null
// 是否显示搜索进度
const isSearchProgressVisible = computed(
() => progressActive.value || (!isRefreshed.value && (progressEnabled.value || progressValue.value > 0)),
@@ -268,10 +270,12 @@ const errorTitle = ref(t('resource.noData'))
const errorDescription = ref(t('resource.noResourceFound'))
let searchEventSource: EventSource | null = null
let searchStreamIdleTimer: ReturnType<typeof setTimeout> | null = null
const streamPreviewLimit = 24
const streamUiFlushDelay = 1000
const streamPreviewBufferLimit = streamPreviewLimit * 4
const searchStreamIdleTimeout = 90_000
const streamTotalCount = ref(0)
const streamPreviewDataList = ref<Array<Context>>([])
@@ -343,6 +347,7 @@ const watchProgressValue = watch(
// 使用SSE监听加载进度
function startLoadingProgress() {
clearProgressResetTimer()
watchProgressValue.resume()
progressText.value = t('resource.searching')
progressValue.value = 0
@@ -357,18 +362,41 @@ function stopLoadingProgress() {
// 确保进度显示100%,然后再渐进清零
progressValue.value = 100
setTimeout(() => {
clearProgressResetTimer()
progressResetTimer = setTimeout(() => {
progressResetTimer = null
progressValue.value = 0
progressEnabled.value = false
}, 1500)
}
function clearProgressResetTimer() {
if (progressResetTimer) {
clearTimeout(progressResetTimer)
progressResetTimer = null
}
}
// 关闭SSE连接
function closeSearchEventSource() {
function closeSearchEventSource(source?: EventSource) {
if (source && searchEventSource !== source) {
source.close()
return
}
if (searchEventSource) {
searchEventSource.close()
searchEventSource = null
}
clearSearchStreamIdleTimer()
}
function clearSearchStreamIdleTimer() {
if (searchStreamIdleTimer) {
clearTimeout(searchStreamIdleTimer)
searchStreamIdleTimer = null
}
}
// 渐进式搜索期间只保留有限预览数据,避免每个批次都触发完整筛选和分组计算。
@@ -607,36 +635,48 @@ function searchByStream(params: SearchParams, requestToken?: string) {
const source = new EventSource(buildSearchStreamUrl(params, requestToken))
searchEventSource = source
const settleSearchStream = (callback: () => void) => {
if (settled) return
settled = true
closeSearchEventSource(source)
callback()
}
const resetIdleTimeout = () => {
clearSearchStreamIdleTimer()
searchStreamIdleTimer = setTimeout(() => {
settleSearchStream(() => reject(new Error(t('resource.noResourceFound'))))
}, searchStreamIdleTimeout)
}
resetIdleTimeout()
source.onmessage = event => {
if (source !== searchEventSource || settled) return
try {
resetIdleTimeout()
const eventData = JSON.parse(event.data)
handleSearchStreamMessage(eventData)
if (eventData.type === 'error') {
settled = true
closeSearchEventSource()
resolve()
settleSearchStream(resolve)
return
}
if (eventData.type === 'done') {
settled = true
closeSearchEventSource()
resolve()
settleSearchStream(resolve)
}
} catch (error) {
settled = true
closeSearchEventSource()
reject(error)
settleSearchStream(() => reject(error))
}
}
source.onerror = () => {
if (settled) return
if (source !== searchEventSource || settled) return
settled = true
closeSearchEventSource()
reject(new Error(t('resource.noResourceFound')))
settleSearchStream(() => reject(new Error(t('resource.noResourceFound'))))
}
})
}
@@ -1011,6 +1051,7 @@ onMounted(async () => {
onUnmounted(() => {
closeSearchEventSource()
stopLoadingProgress()
clearProgressResetTimer()
stopAiRecommendPolling()
clearStreamPreviewState()
})

View File

@@ -1,21 +1,33 @@
export type SSEConnectionStatus = 'idle' | 'connecting' | 'open' | 'error' | 'closed'
export interface SSEManagerOptions {
backgroundCloseDelay: number
reconnectDelay: number
maxReconnectAttempts: number
reconnectBackoffMultiplier: number
maxReconnectDelay: number
}
type SSEMessageListener = (event: MessageEvent) => void
type SSEStatusListener = (status: SSEConnectionStatus) => void
/**
* SSE连接管理器
* 优化后台SSE连接减少iOS系统杀掉应用的概率
* 统一收口 EventSource 生命周期,避免后台常驻连接和重复重连。
*/
export class SSEManager {
private eventSource: EventSource | null = null
private url: string
private isBackground = false
private isBackground = document.hidden
private reconnectTimer: number | null = null
private backgroundCloseTimer: number | null = null
private listeners: Map<string, (event: MessageEvent) => void> = new Map()
private options: {
backgroundCloseDelay: number
reconnectDelay: number
maxReconnectAttempts: number
}
private listeners: Map<string, SSEMessageListener> = new Map()
private statusListeners: Map<string, SSEStatusListener> = new Map()
private options: SSEManagerOptions
private reconnectAttempts = 0
private isConnecting = false
private isDestroyed = false
private connectionStatus: SSEConnectionStatus = 'idle'
private readonly handleVisibilityChange = () => {
if (document.hidden) {
this.handleBackground()
@@ -27,12 +39,14 @@ export class SSEManager {
this.destroy()
}
constructor(url: string, options: Partial<typeof SSEManager.prototype.options> = {}) {
constructor(url: string, options: Partial<SSEManagerOptions> = {}) {
this.url = url
this.options = {
backgroundCloseDelay: 5000, // 5秒后关闭后台连接
reconnectDelay: 3000, // 3秒后重连
maxReconnectAttempts: 3,
reconnectBackoffMultiplier: 1.5,
maxReconnectDelay: 30_000,
...options,
}
@@ -50,38 +64,44 @@ export class SSEManager {
}
private handleBackground() {
if (this.isDestroyed) return
this.isBackground = true
this.clearReconnectTimer()
// 延迟关闭SSE连接避免频繁切换
if (this.backgroundCloseTimer) {
clearTimeout(this.backgroundCloseTimer)
}
this.clearBackgroundCloseTimer()
this.backgroundCloseTimer = window.setTimeout(() => {
if (this.isBackground && this.eventSource) {
this.eventSource.close()
this.eventSource = null
this.closeCurrentEventSource()
this.setConnectionStatus('closed')
}
}, this.options.backgroundCloseDelay)
}
private handleForeground() {
if (this.isDestroyed) return
this.isBackground = false
// 清除后台关闭定时器
if (this.backgroundCloseTimer) {
clearTimeout(this.backgroundCloseTimer)
this.backgroundCloseTimer = null
}
this.clearBackgroundCloseTimer()
// 只有在有活跃监听器时才重新建立连接
if (this.listeners.size > 0 && (!this.eventSource || this.eventSource.readyState === EventSource.CLOSED)) {
this.reconnectSSE()
this.reconnectSSE(0)
}
}
private reconnectSSE(attemptCount = 0) {
if (attemptCount >= this.options.maxReconnectAttempts) {
if (this.isDestroyed || this.isBackground || this.listeners.size === 0) {
return
}
if (attemptCount > this.options.maxReconnectAttempts) {
this.reconnectAttempts = this.options.maxReconnectAttempts
this.setConnectionStatus('closed')
return
}
@@ -89,40 +109,38 @@ export class SSEManager {
return
}
// 如果没有活跃的监听器,不进行重连
if (this.listeners.size === 0) {
return
}
this.clearReconnectTimer()
this.closeCurrentEventSource()
this.isConnecting = true
this.reconnectAttempts = attemptCount
this.setConnectionStatus('connecting')
try {
this.eventSource = new EventSource(this.url)
const source = new EventSource(this.url)
this.eventSource = source
this.eventSource.onopen = () => {
source.onopen = () => {
if (source !== this.eventSource) return
this.isConnecting = false
this.reconnectAttempts = 0
this.setConnectionStatus('open')
}
this.eventSource.onerror = error => {
source.onerror = () => {
if (source !== this.eventSource) return
this.isConnecting = false
this.setConnectionStatus('error')
if (this.eventSource?.readyState === EventSource.CLOSED) {
// 连接已关闭,尝试重连
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer)
}
this.reconnectTimer = window.setTimeout(() => {
if (!this.isBackground && this.listeners.size > 0) {
this.reconnectSSE(this.reconnectAttempts + 1)
}
}, this.options.reconnectDelay)
if (source.readyState === EventSource.CLOSED) {
this.closeCurrentEventSource()
this.scheduleReconnect(this.reconnectAttempts + 1)
}
}
this.eventSource.onmessage = event => {
source.onmessage = event => {
if (source !== this.eventSource || this.isDestroyed) return
// 分发消息给所有监听器
this.listeners.forEach((listener, listenerId) => {
try {
@@ -135,29 +153,95 @@ export class SSEManager {
}
} catch (error) {
this.isConnecting = false
this.setConnectionStatus('error')
// 连接创建失败,尝试重连
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer)
}
this.reconnectTimer = window.setTimeout(() => {
if (!this.isBackground && this.listeners.size > 0) {
this.reconnectSSE(this.reconnectAttempts + 1)
}
}, this.options.reconnectDelay)
this.scheduleReconnect(this.reconnectAttempts + 1)
console.error('SSE: 连接创建失败', error)
}
}
private scheduleReconnect(attemptCount: number) {
if (this.isDestroyed || this.isBackground || this.listeners.size === 0) {
return
}
if (attemptCount > this.options.maxReconnectAttempts) {
this.reconnectAttempts = this.options.maxReconnectAttempts
this.setConnectionStatus('closed')
return
}
this.clearReconnectTimer()
this.reconnectAttempts = attemptCount
// 失败越多等待越久,避免网络波动时短时间内打满连接。
const reconnectDelay = Math.min(
this.options.reconnectDelay * this.options.reconnectBackoffMultiplier ** Math.max(0, attemptCount - 1),
this.options.maxReconnectDelay,
)
this.reconnectTimer = window.setTimeout(() => {
this.reconnectTimer = null
this.reconnectSSE(attemptCount)
}, reconnectDelay)
}
private closeCurrentEventSource() {
if (!this.eventSource) {
return
}
this.eventSource.onopen = null
this.eventSource.onerror = null
this.eventSource.onmessage = null
this.eventSource.close()
this.eventSource = null
this.isConnecting = false
}
private clearReconnectTimer() {
if (!this.reconnectTimer) return
clearTimeout(this.reconnectTimer)
this.reconnectTimer = null
}
private clearBackgroundCloseTimer() {
if (!this.backgroundCloseTimer) return
clearTimeout(this.backgroundCloseTimer)
this.backgroundCloseTimer = null
}
private setConnectionStatus(status: SSEConnectionStatus) {
if (this.connectionStatus === status) return
this.connectionStatus = status
this.statusListeners.forEach((listener, listenerId) => {
try {
listener(status)
} catch (error) {
console.error(`SSE: 状态监听器错误 [${listenerId}]`, error)
}
})
}
/**
* 添加消息监听器
*/
addMessageListener(id: string, listener: (event: MessageEvent) => void) {
addMessageListener(id: string, listener: SSEMessageListener) {
if (this.isDestroyed) return
this.listeners.set(id, listener)
// 如果还没有连接且不在后台,现在建立连接
if (!this.eventSource && !this.isBackground && !this.isConnecting) {
this.reconnectSSE()
if (
!this.isBackground &&
!this.isConnecting &&
(!this.eventSource || this.eventSource.readyState === EventSource.CLOSED)
) {
this.reconnectSSE(0)
}
}
@@ -165,6 +249,8 @@ export class SSEManager {
* 移除消息监听器
*/
removeMessageListener(id: string) {
if (this.isDestroyed) return
this.listeners.delete(id)
// 如果没有监听器了,关闭连接
@@ -184,25 +270,17 @@ export class SSEManager {
* 销毁管理器并清理所有引用
*/
destroy() {
if (this.isDestroyed) return
this.isDestroyed = true
this.resetConnectionState(true)
this.removeVisibilityListener()
}
private resetConnectionState(clearListeners = false) {
if (this.eventSource) {
this.eventSource.close()
this.eventSource = null
}
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer)
this.reconnectTimer = null
}
if (this.backgroundCloseTimer) {
clearTimeout(this.backgroundCloseTimer)
this.backgroundCloseTimer = null
}
this.closeCurrentEventSource()
this.clearReconnectTimer()
this.clearBackgroundCloseTimer()
if (clearListeners) {
this.listeners.clear()
@@ -210,6 +288,31 @@ export class SSEManager {
this.isConnecting = false
this.reconnectAttempts = 0
this.setConnectionStatus(this.listeners.size > 0 ? 'closed' : 'idle')
if (clearListeners) {
this.statusListeners.clear()
}
}
/**
* 添加连接状态监听器
*/
addStatusListener(id: string, listener: SSEStatusListener, emitCurrent = true) {
if (this.isDestroyed) return
this.statusListeners.set(id, listener)
if (emitCurrent) {
listener(this.connectionStatus)
}
}
/**
* 移除连接状态监听器
*/
removeStatusListener(id: string) {
this.statusListeners.delete(id)
}
/**
@@ -219,6 +322,13 @@ export class SSEManager {
return this.eventSource?.readyState ?? EventSource.CLOSED
}
/**
* 获取内部连接状态
*/
get status(): SSEConnectionStatus {
return this.connectionStatus
}
/**
* 获取连接URL
*/
@@ -230,10 +340,12 @@ export class SSEManager {
* 强制重新连接
*/
forceReconnect() {
if (this.isDestroyed) return
const hasActiveListeners = this.listeners.size > 0
this.close()
if (!this.isBackground && hasActiveListeners) {
this.reconnectSSE()
this.reconnectSSE(0)
}
}

View File

@@ -3,11 +3,11 @@ import { useTheme } from 'vuetify'
import { hexToRgb } from '@layouts/utils'
import api from '@/api'
import { useI18n } from 'vue-i18n'
import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization'
import { useBackground } from '@/composables/useBackground'
// 国际化
const { t } = useI18n()
const { useDataRefresh } = useBackgroundOptimization()
const { useDataRefresh } = useBackground()
// 输入参数
const props = defineProps({
@@ -122,7 +122,7 @@ async function loadCpuData() {
}
}
// 使用优化的数据刷新定时器
// 使用数据刷新定时器
const { loading } = useDataRefresh(
'analytics-cpu',
loadCpuData,

View File

@@ -4,11 +4,11 @@ import { hexToRgb } from '@layouts/utils'
import api from '@/api'
import { formatBytes } from '@/@core/utils/formatters'
import { useI18n } from 'vue-i18n'
import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization'
import { useBackground } from '@/composables/useBackground'
// 国际化
const { t } = useI18n()
const { useDataRefresh } = useBackgroundOptimization()
const { useDataRefresh } = useBackground()
// 输入参数
const props = defineProps({
@@ -127,7 +127,7 @@ async function loadMemoryData() {
}
}
// 使用优化的数据刷新定时器
// 使用数据刷新定时器
const { loading } = useDataRefresh(
'analytics-memory',
loadMemoryData,

View File

@@ -3,11 +3,11 @@ import { useTheme } from 'vuetify'
import { hexToRgb } from '@layouts/utils'
import api from '@/api'
import { useI18n } from 'vue-i18n'
import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization'
import { useBackground } from '@/composables/useBackground'
// 国际化
const { t } = useI18n()
const { useDataRefresh } = useBackgroundOptimization()
const { useDataRefresh } = useBackground()
// 输入参数
const props = defineProps({
@@ -160,7 +160,7 @@ async function getNetworkUsage() {
}
}
// 使用优化的数据刷新定时器
// 使用数据刷新定时器
useDataRefresh(
'dashboard-network',
getNetworkUsage,

View File

@@ -3,11 +3,11 @@ import { formatSeconds } from '@/@core/utils/formatters'
import api from '@/api'
import type { Process } from '@/api/types'
import { useI18n } from 'vue-i18n'
import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization'
import { useBackground } from '@/composables/useBackground'
// 国际化
const { t } = useI18n()
const { useDataRefresh } = useBackgroundOptimization()
const { useDataRefresh } = useBackground()
// 表头
const headers = [
@@ -31,7 +31,7 @@ async function loadProcessList() {
}
}
// 使用优化的数据刷新定时器
// 使用数据刷新定时器
useDataRefresh(
'dashboard-processes',
loadProcessList,

View File

@@ -2,11 +2,11 @@
import api from '@/api'
import type { ScheduleInfo } from '@/api/types'
import { useI18n } from 'vue-i18n'
import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization'
import { useBackground } from '@/composables/useBackground'
// 国际化
const { t } = useI18n()
const { useDataRefresh } = useBackgroundOptimization()
const { useDataRefresh } = useBackground()
// 输入参数
const props = defineProps({
@@ -34,7 +34,7 @@ async function loadSchedulerList() {
}
}
// 使用优化的数据刷新定时器
// 使用数据刷新定时器
useDataRefresh(
'dashboard-scheduler',
loadSchedulerList,

View File

@@ -3,11 +3,11 @@ import { formatFileSize } from '@/@core/utils/formatters'
import api from '@/api'
import type { DownloaderInfo } from '@/api/types'
import { useI18n } from 'vue-i18n'
import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization'
import { useBackground } from '@/composables/useBackground'
// 国际化
const { t } = useI18n()
const { useDataRefresh } = useBackgroundOptimization()
const { useDataRefresh } = useBackground()
// 输入参数
const props = defineProps({
@@ -77,7 +77,7 @@ async function loadDownloaderInfo() {
}
}
// 使用优化的数据刷新定时器
// 使用数据刷新定时器
const { loading } = useDataRefresh(
'analytics-speed',
loadDownloaderInfo,

View File

@@ -7,11 +7,11 @@ import DownloadingCard from '@/components/cards/DownloadingCard.vue'
import ProgressiveCardGrid from '@/components/misc/ProgressiveCardGrid.vue'
import { useUserStore } from '@/stores'
import { useI18n } from 'vue-i18n'
import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization'
import { useBackground } from '@/composables/useBackground'
// 国际化
const { t } = useI18n()
const { useDataRefresh } = useBackgroundOptimization()
const { useDataRefresh } = useBackground()
// 定义输入参数
const props = defineProps<{
@@ -56,7 +56,7 @@ const filteredDataList = computed(() => {
else return dataList.value.filter(data => data.userid === userName || data.username === userName)
})
// 使用优化的数据刷新定时器
// 使用数据刷新定时器
const { loading: dataLoading } = useDataRefresh(
'downloading-list',
fetchData,

View File

@@ -14,7 +14,7 @@ import { useI18n } from 'vue-i18n'
import { usePWA } from '@/composables/usePWA'
import { useDynamicButton } from '@/composables/useDynamicButton'
import { useAvailableHeight } from '@/composables/useAvailableHeight'
import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization'
import { useBackground } from '@/composables/useBackground'
import { useGlobalSettingsStore } from '@/stores'
// i18n
@@ -27,7 +27,7 @@ const globalSettingsStore = useGlobalSettingsStore()
const display = useDisplay()
// PWA模式检测
const { appMode } = usePWA()
const { useProgressSSE } = useBackgroundOptimization()
const { useProgressSSE } = useBackground()
// 计算列表可用高度
// componentOffset = VCardItem搜索栏(68) + VDivider(1) + 分页栏(40) + VCard边距(2) = 111

View File

@@ -1,7 +1,7 @@
<script lang="ts" setup>
import { useI18n } from 'vue-i18n'
import { useTheme } from 'vuetify'
import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization'
import { useBackground } from '@/composables/useBackground'
import { useAvailableHeight } from '@/composables/useAvailableHeight'
import { useDisplay } from 'vuetify'
@@ -46,7 +46,7 @@ const props = defineProps<{
const { t } = useI18n()
const theme = useTheme()
const display = useDisplay()
const { useSSE } = useBackgroundOptimization()
const { useSSE } = useBackground()
const DEFAULT_LEVELS = ['TRACE', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
const MAX_LOG_LINES = 600

View File

@@ -3,11 +3,11 @@ import type { Message } from '@/api/types'
import MessageCard from '@/components/cards/MessageCard.vue'
import api from '@/api'
import { useI18n } from 'vue-i18n'
import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization'
import { useBackground } from '@/composables/useBackground'
// 国际化
const { t } = useI18n()
const { useSSE } = useBackgroundOptimization()
const { useSSE } = useBackground()
// 消息列表
const messages = ref<Message[]>([])
@@ -201,7 +201,7 @@ function handleSSEMessage(event: MessageEvent) {
}
}
// 使用优化的SSE连接
// 使用SSE连接
const { manager, isConnected } = useSSE(
`${import.meta.env.VITE_API_BASE_URL}system/message?role=user`,
handleSSEMessage,

View File

@@ -3,11 +3,11 @@ import { useToast } from 'vue-toastification'
import api from '@/api'
import type { ScheduleInfo } from '@/api/types'
import { useI18n } from 'vue-i18n'
import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization'
import { useBackground } from '@/composables/useBackground'
// 国际化
const { t } = useI18n()
const { useDataRefresh } = useBackgroundOptimization()
const { useDataRefresh } = useBackground()
// 提示框
const $toast = useToast()
@@ -59,7 +59,7 @@ function runCommand(id: string) {
}
}
// 使用优化的数据刷新定时器
// 使用数据刷新定时器
useDataRefresh(
'scheduler-list',
loadSchedulerList,