From b01421aa94a4c0e1e23874c0687b2412b2cdd626 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Mon, 30 Jun 2025 20:38:50 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=BB=84=E4=BB=B6=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.vue | 109 ++++++++++-------- src/components/dialog/SiteUserDataDialog.vue | 9 +- src/views/dashboard/AnalyticsCpu.vue | 15 ++- src/views/dashboard/AnalyticsMemory.vue | 18 ++- .../dashboard/AnalyticsWeeklyOverview.vue | 13 ++- 5 files changed, 105 insertions(+), 59 deletions(-) diff --git a/src/App.vue b/src/App.vue index 8ed919d0..c6f84098 100644 --- a/src/App.vue +++ b/src/App.vue @@ -41,26 +41,33 @@ declare global { } } -if (window.Apex) { - // 数据标签 - window.Apex.dataLabels = { - formatter: function (_: number, { seriesIndex, w }: { seriesIndex: number; w: any }) { - // 如果有小数点,保留两位小数,否则保留整数 - const data = w.config.series[seriesIndex] - return data.toFixed(data % 1 === 0 ? 0 : 1) - }, - } - // 图例 - window.Apex.legend = { - labels: { - useSeriesColors: true, - }, - } - // 标题 - window.Apex.title = { - style: { - color: 'rgba(var(--v-theme-on-surface), var(--v-high-emphasis-opacity))', - }, +// 配置 ApexCharts 全局选项 +function configureApexCharts() { + if (typeof window !== 'undefined' && window.Apex) { + try { + // 数据标签 + window.Apex.dataLabels = { + formatter: function (_: number, { seriesIndex, w }: { seriesIndex: number; w: any }) { + // 如果有小数点,保留两位小数,否则保留整数 + const data = w.config.series[seriesIndex] + return data.toFixed(data % 1 === 0 ? 0 : 1) + }, + } + // 图例 + window.Apex.legend = { + labels: { + useSeriesColors: true, + }, + } + // 标题 + window.Apex.title = { + style: { + color: 'rgba(var(--v-theme-on-surface), var(--v-high-emphasis-opacity))', + }, + } + } catch (error) { + console.warn('ApexCharts 全局配置失败:', error) + } } } @@ -74,10 +81,13 @@ function updateHtmlThemeAttribute(themeName: string) { // 获取背景图片 async function fetchBackgroundImages() { try { - backgroundImages.value = await api.get(`/login/wallpapers`) + const controller = new AbortController() + backgroundImages.value = await api.get(`/login/wallpapers`, { + signal: controller.signal, + }) activeImageIndex.value = 0 } catch (e) { - console.error(e) + throw e } } @@ -85,7 +95,6 @@ async function fetchBackgroundImages() { function startBackgroundRotation() { // 清除轮换定时器 if (backgroundRotationTimer) clearInterval(backgroundRotationTimer) - if (backgroundImages.value.length > 1) { backgroundRotationTimer = setInterval(() => { // 计算下一个图片索引 @@ -131,7 +140,6 @@ function animateAndRemoveLoader() { if (loadingBg) { // 先添加完成动画类 loadingBg.classList.add('loading-complete') - // 等待动画完成后再移除元素 setTimeout(() => { removeEl('#loading-bg') @@ -144,20 +152,27 @@ function animateAndRemoveLoader() { } // 加载背景图片 -async function loadBackgroundImages() { - await fetchBackgroundImages() - .then(() => { - startBackgroundRotation() - }) - .catch(() => { - // 3秒后重试 +async function loadBackgroundImages(retryCount = 0) { + const maxRetries = 3 + try { + await fetchBackgroundImages() + startBackgroundRotation() + } catch (error: any) { + const isAbortError = error.name === 'AbortError' || error.code === 'ERR_CANCELED' + if (retryCount < maxRetries) { + const baseDelay = isAbortError ? 1000 : 3000 + const retryDelay = Math.min(baseDelay * Math.pow(2, retryCount), 10000) setTimeout(() => { - loadBackgroundImages() - }, 3000) - }) + loadBackgroundImages(retryCount + 1) + }, retryDelay) + } + } } onMounted(async () => { + // 配置 ApexCharts + configureApexCharts() + // 初始化data-theme属性 updateHtmlThemeAttribute(globalTheme.name.value) @@ -165,7 +180,7 @@ onMounted(async () => { show.value = false // 加载背景图片 - await loadBackgroundImages() + loadBackgroundImages() // 移除加载动画 ensureRenderComplete(() => { @@ -173,7 +188,6 @@ onMounted(async () => { setTimeout(() => { // 移除加载动画,显示页面 animateAndRemoveLoader() - // 页面完全显示后,检查未读消息 setTimeout(() => { checkAndEmitUnreadMessages() @@ -185,11 +199,14 @@ onMounted(async () => { // 添加页面可见性变化监听 document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible') { - loadBackgroundImages() - // 页面恢复可见时检查未读消息 + // 页面恢复可见时,稍作延迟以确保状态稳定 setTimeout(() => { - checkAndEmitUnreadMessages() - }, 500) + loadBackgroundImages() + // 检查未读消息 + setTimeout(() => { + checkAndEmitUnreadMessages() + }, 300) + }, 100) } }) @@ -197,11 +214,14 @@ onMounted(async () => { window.addEventListener('pageshow', event => { // persisted属性为true表示页面是从bfcache中恢复的 if (event.persisted) { - loadBackgroundImages() - // PWA恢复时检查未读消息 + // PWA恢复时,稍作延迟以确保状态稳定 setTimeout(() => { - checkAndEmitUnreadMessages() - }, 500) + loadBackgroundImages() + // 检查未读消息 + setTimeout(() => { + checkAndEmitUnreadMessages() + }, 300) + }, 100) } }) }) @@ -211,7 +231,6 @@ onUnmounted(() => { document.removeEventListener('visibilitychange', () => {}) // 移除PWA的页面恢复事件监听 window.removeEventListener('pageshow', () => {}) - // 清除轮换定时器 if (backgroundRotationTimer) { clearInterval(backgroundRotationTimer) diff --git a/src/components/dialog/SiteUserDataDialog.vue b/src/components/dialog/SiteUserDataDialog.vue index e7aec9ad..fbcd013d 100644 --- a/src/components/dialog/SiteUserDataDialog.vue +++ b/src/components/dialog/SiteUserDataDialog.vue @@ -253,6 +253,8 @@ async function fetchSiteUserData() { try { const result: { [key: string]: any } = await api.get(`site/userdata/${props.site?.id}`) if (result.success) { + // 使用nextTick确保DOM更新完成后再更新图表数据 + await nextTick() siteDatas.value = result.data.sort((a: { updated_day: any }, b: { updated_day: any }) => (a.updated_day || '').localeCompare(b.updated_day || ''), ) @@ -276,8 +278,11 @@ async function refreshSiteData() { progressDialog.value = false } -onBeforeMount(async () => { - await fetchSiteUserData() +onBeforeMount(() => { + // 延迟加载,确保组件完全挂载 + nextTick(() => { + fetchSiteUserData() + }) }) diff --git a/src/views/dashboard/AnalyticsCpu.vue b/src/views/dashboard/AnalyticsCpu.vue index 91938012..5ddd7aee 100644 --- a/src/views/dashboard/AnalyticsCpu.vue +++ b/src/views/dashboard/AnalyticsCpu.vue @@ -112,6 +112,8 @@ async function getCpuUsage() { try { // 请求数据 current.value = (await api.get('dashboard/cpu')) ?? 0 + // 使用nextTick确保DOM更新完成后再更新图表数据 + await nextTick() // 添加到序列 series.value[0].data.push(current.value) // 序列超过30条记录时,清掉前面的 @@ -122,10 +124,13 @@ async function getCpuUsage() { } onMounted(() => { - getCpuUsage() // 启动定时器 - refreshTimer = setInterval(() => { + // 延迟启动,确保组件完全挂载 + nextTick(() => { getCpuUsage() - }, 2000) + refreshTimer = setInterval(() => { + getCpuUsage() + }, 2000) + }) }) // 组件卸载时停止定时器 @@ -137,7 +142,9 @@ onUnmounted(() => { }) onActivated(() => { - chartKey.value += 1 + nextTick(() => { + chartKey.value += 1 + }) }) diff --git a/src/views/dashboard/AnalyticsMemory.vue b/src/views/dashboard/AnalyticsMemory.vue index 3c2a4e3e..5492e412 100644 --- a/src/views/dashboard/AnalyticsMemory.vue +++ b/src/views/dashboard/AnalyticsMemory.vue @@ -118,6 +118,8 @@ async function getMemorgUsage() { try { // 请求数据 ;[usedMemory.value, memoryUsage.value] = await api.get('dashboard/memory') + // 使用nextTick确保DOM更新完成后再更新图表数据 + await nextTick() series.value[0].data.push(memoryUsage.value) // 序列超过30条记录时,清掉前面的 if (series.value[0].data.length > 30) series.value[0].data.shift() @@ -127,11 +129,14 @@ async function getMemorgUsage() { } onMounted(() => { - getMemorgUsage() - // 启动定时器 - refreshTimer = setInterval(() => { + // 延迟启动,确保组件完全挂载 + nextTick(() => { getMemorgUsage() - }, 3000) + // 启动定时器 + refreshTimer = setInterval(() => { + getMemorgUsage() + }, 3000) + }) }) // 组件卸载时停止定时器 @@ -143,7 +148,10 @@ onUnmounted(() => { }) onActivated(() => { - chartKey.value += 1 + // 使用nextTick确保DOM准备完成后再更新chartKey + nextTick(() => { + chartKey.value += 1 + }) }) diff --git a/src/views/dashboard/AnalyticsWeeklyOverview.vue b/src/views/dashboard/AnalyticsWeeklyOverview.vue index 88a5ca85..65a2db19 100644 --- a/src/views/dashboard/AnalyticsWeeklyOverview.vue +++ b/src/views/dashboard/AnalyticsWeeklyOverview.vue @@ -107,7 +107,8 @@ const totalCount = computed(() => series.value[0].data.reduce((a, b) => a + b, 0 async function getWeeklyData() { try { const res: number[] = await api.get('dashboard/transfer') - + // 使用nextTick确保DOM更新完成后再更新图表数据 + await nextTick() series.value = [{ data: res }] } catch (e) { console.log(e) @@ -115,11 +116,17 @@ async function getWeeklyData() { } onMounted(() => { - getWeeklyData() + // 延迟启动,确保组件完全挂载 + nextTick(() => { + getWeeklyData() + }) }) onActivated(() => { - getWeeklyData() + // 使用nextTick确保DOM准备完成后再获取数据 + nextTick(() => { + getWeeklyData() + }) })