perf(admin): 优化管理端页面切换性能与包体积

- 阶段 A: 增加 KeepAlive 缓存高频列表页,改用 onActivated 进行后台静默刷新,优化 tab 切换与 HomeEntry 闪屏

- 阶段 B: beforeEach 守卫在 TTL 内走同步快速路径,api 请求拦截器缓存 token 避免重复解析 JWT

- 阶段 C: 引入 unplugin 插件启用 Element Plus 组件按需加载,清理 App.vue 中 960+ 行冗余暗色主题 CSS

- i18n 拆包: 将三语文案包提取为独立懒加载 chunks,主包体积从 209KB 减少至 99KB (降低 53%)
This commit is contained in:
2026-06-18 15:30:27 +08:00
parent 4244c9e10b
commit 5baac41ce8
26 changed files with 549 additions and 603 deletions

View File

@@ -1,5 +1,7 @@
<script setup lang="ts">
import { ref, onMounted, computed, watch, reactive, h } from 'vue';
import { ref, onMounted, onActivated, computed, watch, reactive, h } from 'vue';
defineOptions({ name: 'AdminAgentManager' });
import { useRouter } from 'vue-router';
import { ElMessage, ElMessageBox } from 'element-plus';
import { useAdminLocale } from '../composables/useAdminLocale';
@@ -423,6 +425,11 @@ onMounted(() => {
loadTier1Agents();
}
});
// KeepAlive 激活时静默刷新列表不重复page-init
onActivated(() => {
if (canViewUsers.value && allPlayers.value.length > 0) void loadAllPlayers();
if (canViewAgents.value && tier1Agents.value.length > 0) void loadTier1Agents();
});
async function loadUsersPageInit() {
try {

View File

@@ -1,5 +1,7 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { ref, onMounted, onActivated } from 'vue';
defineOptions({ name: 'AdminBets' });
import api from '../api';
import { formatAmount, formatAmountFull } from '../utils/format-amount';
import {
@@ -33,6 +35,8 @@ const detail = ref<BetDetail | null>(null);
const detailLoading = ref(false);
onMounted(load);
// KeepAlive 激活时:有缓存数据则后台静默刷新,避免白屏等待
onActivated(() => { if (bets.value.length > 0) void load(); });
function betContentCounts(row: BetListRow) {
const singles = row.betType === 'SINGLE' ? 1 : 0;

View File

@@ -1,5 +1,7 @@
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import { computed, onMounted, onActivated, ref } from 'vue';
defineOptions({ name: 'AdminCashback' });
import type { TableColumnCtx } from 'element-plus';
import { ElMessage, ElMessageBox } from 'element-plus';
import { useAdminLocale } from '../composables/useAdminLocale';
@@ -249,6 +251,8 @@ function onHistoryStatusChange() {
}
onMounted(loadHistory);
// KeepAlive 激活时静默刷新
onActivated(() => { if (history.value.length > 0) void loadHistory(); });
</script>
<template>

View File

@@ -44,8 +44,8 @@ function switchTab(key: string) {
</button>
</div>
<DepositOrders v-if="activeTab === 'orders'" />
<PaymentMethods v-if="activeTab === 'methods'" />
<DepositOrders v-show="activeTab === 'orders'" />
<PaymentMethods v-show="activeTab === 'methods'" />
</div>
</template>

View File

@@ -1,5 +1,7 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { ref, onMounted, onActivated } from 'vue';
defineOptions({ name: 'AdminDepositOrders' });
import { ElMessage, ElMessageBox } from 'element-plus';
import { useAdminLocale } from '../composables/useAdminLocale';
import { resolveApiError } from '../i18n/form-validation';
@@ -248,6 +250,8 @@ function prevPage() { if (page.value > 1) { page.value--; fetchList(); } }
function nextPage() { if (page.value * pageSize.value < total.value) { page.value++; fetchList(); } }
onMounted(fetchList);
// KeepAlive 激活时静默刷新
onActivated(() => { if (items.value.length > 0) void fetchList(); });
</script>
<template>

View File

@@ -1,5 +1,7 @@
<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue';
import { ref, computed, onMounted, onActivated, watch } from 'vue';
defineOptions({ name: 'AdminFinanceLogs' });
import { useRoute, useRouter } from 'vue-router';
import { useAuthStore } from '../stores/auth';
import { useAdminLocale } from '../composables/useAdminLocale';
@@ -204,6 +206,11 @@ onMounted(() => {
if (activeTab.value === 'credit') void loadCredit();
else void loadTransfer();
});
// KeepAlive 激活时静默刷新
onActivated(() => {
if (activeTab.value === 'credit' && creditItems.value.length > 0) void loadCredit();
else if (activeTab.value === 'transfer' && transferItems.value.length > 0) void loadTransfer();
});
watch(
() => route.query.agentId,

View File

@@ -3,7 +3,6 @@ import { shallowRef, onBeforeMount, type Component } from 'vue';
import { RouterView, RouterLink, useRoute, useRouter } from 'vue-router';
import { useAdminLocale } from '../composables/useAdminLocale';
import { useAuthStore } from '../stores/auth';
import { ensureStaffSession } from '../utils/session-hydrate';
import DashboardSubNav from '../components/DashboardSubNav.vue';
import { usePermissions } from '../composables/usePermissions';
import { AdminPerm } from '../constants/permissions';
@@ -14,12 +13,11 @@ const router = useRouter();
const { t } = useAdminLocale();
const { hasPermission, role } = usePermissions();
const agentDashboard = shallowRef<Component | null>(null);
const booting = shallowRef(true);
const showSupportShortcuts = shallowRef(false);
onBeforeMount(async () => {
await ensureStaffSession();
// router beforeEach 已执行过 ensureStaffSession此处仅做菜单路由重定向逻辑不再重复远程请求
if (auth.isAdmin.value) {
const code = role.value;
if (code === 'FINANCE_ADMIN' && route.path === '/') {
@@ -37,13 +35,11 @@ onBeforeMount(async () => {
} else {
agentDashboard.value = (await import('./agent/Dashboard.vue')).default;
}
booting.value = false;
});
</script>
<template>
<div v-if="booting" v-loading="true" class="home-boot" />
<template v-else-if="auth.isAdmin.value">
<template v-if="auth.isAdmin.value">
<div v-if="showSupportShortcuts" class="support-shortcuts">
<RouterLink v-if="hasPermission(AdminPerm.usersView, AdminPerm.agentsView)" to="/users" class="support-link">
{{ t('nav.agents_players') }}

View File

@@ -1,5 +1,7 @@
<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
import { ref, computed, onMounted, onActivated, onBeforeUnmount } from 'vue';
defineOptions({ name: 'AdminMatches' });
import { useRoute } from 'vue-router';
import { useAdminLocale } from '../composables/useAdminLocale';
import { resolveFormError } from '../i18n/form-validation';
@@ -121,6 +123,8 @@ onMounted(() => {
}
load({ restoreExpand: true });
});
// KeepAlive 激活时静默刷新,保持展开状态
onActivated(() => { if (leagues.value.length > 0) void load({ keepExpand: true }); });
onBeforeUnmount(persistListUiState);
function onPageChange(p: number) {

View File

@@ -1,5 +1,7 @@
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { ref, onMounted, onActivated, onBeforeUnmount } from 'vue';
defineOptions({ name: 'AdminMatchesOutrights' });
import { useRoute } from 'vue-router';
import { useAdminLocale } from '../composables/useAdminLocale';
import api from '../api';
@@ -97,6 +99,8 @@ onMounted(async () => {
await load({ restoreExpand: true });
await resolveExpandFromQuery();
});
// KeepAlive 激活时静默刷新
onActivated(() => { if (leagues.value.length > 0) void load({ keepExpand: true }); });
onBeforeUnmount(persistListUiState);
function onPageChange(p: number) {

View File

@@ -1,5 +1,7 @@
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import { computed, onMounted, onActivated, ref } from 'vue';
defineOptions({ name: 'AdminStaffManage' });
import { ElMessage, ElMessageBox } from 'element-plus';
import api from '../api';
import { useAdminLocale } from '../composables/useAdminLocale';
@@ -249,6 +251,8 @@ onMounted(async () => {
await loadRoles();
await load();
});
// KeepAlive 激活时静默刷新
onActivated(() => { if (rows.value.length > 0) void load(); });
</script>
<template>