This commit is contained in:
2026-08-05 15:54:59 +08:00
parent eede351038
commit 4b31eb7786
43 changed files with 2117 additions and 851 deletions
+58 -3
View File
@@ -7,15 +7,19 @@ import { getApiErrorDetails } from '@/shared/api/http'
import baiyueLogoWhite from '@/assets/brand/baiyue-logo-white.png'
import baiyueMarkWhite from '@/assets/brand/baiyue-mark-white.png'
import AdminSidebarMenu from '@/app/components/AdminSidebarMenu.vue'
import WorkspaceTabs from '@/app/components/WorkspaceTabs.vue'
import { useAccessPreviewStore } from '@/core/access/accessPreview'
import { useAuthStore } from '@/core/auth/auth'
import { useUnsavedWorkStore } from '@/core/unsaved-work/unsavedWork'
import { canAccessWorkspaceRoute, workspaceRouteId, workspaceTabCandidate } from '@/core/workspace-tabs/routeWorkspace'
import { MAX_CACHED_WORKSPACE_VIEWS, useWorkspaceTabsStore } from '@/core/workspace-tabs/workspaceTabs'
const route = useRoute()
const router = useRouter()
const auth = useAuthStore()
const access = useAccessPreviewStore()
const unsaved = useUnsavedWorkStore()
const workspace = useWorkspaceTabsStore()
const collapsed = ref(false)
const mobileMenuOpen = ref(false)
const pageTitle = computed(() => route.meta.title || '管理后台')
@@ -24,10 +28,38 @@ const showPageHeading = computed(() => route.meta.showPageHeading !== false)
const menuCollapsed = computed(() => collapsed.value && !mobileMenuOpen.value)
access.initializeAfterLogin()
workspace.initialize(auth.user?.id || auth.user?.account || '')
function syncCurrentRoute() {
const candidate = workspaceTabCandidate(route)
if (candidate) workspace.open(candidate, unsaved.dirtyWorkspaceIds)
}
async function pruneInaccessibleTabs() {
const inaccessible = workspace.tabs.filter((tab) => !canAccessWorkspaceRoute(router.resolve(tab.path), access.permissionCodes))
const discardedDirtyWork = inaccessible.flatMap((tab) => unsaved.findForWorkspace(tab.id))
if (discardedDirtyWork.length) {
await ElMessageBox.alert(
`账号权限已变化,以下未保存内容无法继续编辑:${[...new Set(discardedDirtyWork.map((entry) => entry.label))].join('、')}。相关页签将关闭。`,
'权限已变化',
{ confirmButtonText: '知道了', type: 'warning' },
).catch(() => undefined)
}
const previousActiveId = workspace.activeId
const result = workspace.prune((tab) => canAccessWorkspaceRoute(router.resolve(tab.path), access.permissionCodes))
if (result.removedIds.length === 0) return
unsaved.removeWorkspaces(result.removedIds)
if (!previousActiveId || !result.removedIds.includes(previousActiveId)) return
const destination = result.fallback?.path || access.firstAccessiblePath || '/403'
if (destination !== route.fullPath) await router.replace(destination)
else syncCurrentRoute()
}
async function handleLogout() {
try {
await ElMessageBox.confirm('确定退出管理后台吗?', '退出登录', {
const dirtyLabels = unsaved.entries.filter((entry) => entry.dirty).map((entry) => entry.label)
const dirtyNotice = dirtyLabels.length ? ` 当前还有未保存内容:${[...new Set(dirtyLabels)].join('、')},退出后将丢失。` : ''
await ElMessageBox.confirm(`确定退出管理后台吗?${dirtyNotice}`, '退出登录', {
confirmButtonText: '退出',
cancelButtonText: '取消',
})
@@ -37,6 +69,7 @@ async function handleLogout() {
access.reset()
unsaved.clear()
workspace.reset(true)
try {
await auth.logout()
} catch (error) {
@@ -59,7 +92,17 @@ function handleMenuToggle() {
collapsed.value = !collapsed.value
}
watch(() => route.fullPath, () => { mobileMenuOpen.value = false })
watch(() => route.fullPath, () => {
mobileMenuOpen.value = false
syncCurrentRoute()
}, { immediate: true })
watch(() => access.permissionSignature, () => { void pruneInaccessibleTabs() }, { immediate: true })
watch(() => auth.user?.id || auth.user?.account || '', (userId, previousUserId) => {
if (!userId || userId === previousUserId) return
workspace.reset(false)
workspace.initialize(userId)
void pruneInaccessibleTabs().then(syncCurrentRoute)
})
</script>
<template>
@@ -91,7 +134,19 @@ watch(() => route.fullPath, () => { mobileMenuOpen.value = false })
<el-button text aria-label="退出登录" @click="handleLogout"><LogOut :size="18" /></el-button>
</div>
</header>
<main class="page-container"><RouterView /></main>
<WorkspaceTabs v-if="workspace.tabs.length" />
<main class="page-container">
<RouterView v-slot="{ Component, route: renderedRoute }">
<KeepAlive :max="MAX_CACHED_WORKSPACE_VIEWS">
<component
:is="Component"
v-if="renderedRoute.meta.keepAlive !== false"
:key="workspace.cacheKey(workspaceRouteId(renderedRoute))"
/>
</KeepAlive>
<component :is="Component" v-if="renderedRoute.meta.keepAlive === false" />
</RouterView>
</main>
</div>
</div>
</template>