Files
verify/frontend/src/layouts/admin.vue
T

182 lines
5.2 KiB
Vue

<script setup lang="ts">
import { computed, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import AdminSidebar from '@/components/admin-sidebar/index.vue'
import LanguageChange from '@/components/language-change.vue'
import ThemePopover from '@/components/custom-theme/theme-popover.vue'
import ToggleTheme from '@/components/toggle-theme.vue'
import api from '@/services/api'
import { getFileUrl } from '@/utils/config'
const router = useRouter()
const breadcrumbs = computed(() => {
const path = router.currentRoute.value.path
const parts = path.split('/').filter(Boolean)
const crumbs = [{ title: '控制台', path: '/admin' }]
const titleMap: Record<string, string> = {
applications: '应用管理',
cards: '卡密管理',
users: '用户管理',
devices: '设备管理',
sessions: '在线实例',
announcements: '公告管理',
versions: '版本管理',
'card-types': '卡类管理',
'agent-apps': '授权管理',
agents: '代理管理',
finance: '财务管理',
logs: '日志记录',
tickets: '工单系统',
'cloud-constants': '云端常量',
'cloud-variables': '云端变量',
'cloud-function': '云端函数',
'risk-control': '风控管理',
extension: '扩展配置',
profile: '个人中心',
'system-settings': '系统设置',
'payment-channels': '支付渠道',
'email-settings': '邮箱配置',
'sms-settings': '短信配置',
'storage-configs': '存储管理',
settings: '基本设置',
security: '安全设置',
create: '创建',
edit: '编辑',
recharge: '充值',
request: '申请授权',
invite: '邀请授权',
}
let currentPath = '/admin'
for (let i = 1; i < parts.length; i++) {
const part = parts[i]
currentPath += `/${part}`
if (titleMap[part]) {
crumbs.push({
title: titleMap[part],
path: currentPath,
})
}
else if (!isNaN(Number(part)) && i === parts.length - 1) {
crumbs.push({
title: '详情',
path: currentPath,
})
}
}
return crumbs
})
function getAssetUrl(path: string) {
return getFileUrl(path)
}
function updateFavicon(favicon: string) {
if (!favicon)
return
const link: HTMLLinkElement = document.querySelector('link[rel*=\'icon\']') || document.createElement('link')
link.type = 'image/x-icon'
link.rel = 'shortcut icon'
link.href = getAssetUrl(favicon)
document.getElementsByTagName('head')[0].appendChild(link)
}
function updateSiteName(name: string) {
if (name)
document.title = name
}
function applySettings(settings: { site_name?: string, site_logo?: string, site_favicon?: string }) {
if (settings.site_favicon)
updateFavicon(settings.site_favicon)
if (settings.site_name)
updateSiteName(settings.site_name)
}
function handleSettingsChange(event: CustomEvent) {
applySettings(event.detail)
}
async function loadSystemSettings() {
const storedSettings = localStorage.getItem('systemSettings')
if (storedSettings) {
try {
applySettings(JSON.parse(storedSettings))
}
catch (e) {
console.error('Failed to parse systemSettings', e)
}
return
}
try {
const data = await api.get<any>('/dev/system-settings')
if (data) {
const settings = {
site_name: data.site_name || '',
site_logo: data.site_logo || '',
site_favicon: data.site_favicon || '',
}
localStorage.setItem('systemSettings', JSON.stringify(settings))
window.dispatchEvent(new CustomEvent('system-settings-changed', { detail: settings }))
}
}
catch (error) {
console.error('加载系统设置失败:', error)
}
}
onMounted(() => {
loadSystemSettings()
window.addEventListener('system-settings-changed', handleSettingsChange as EventListener)
})
onUnmounted(() => {
window.removeEventListener('system-settings-changed', handleSettingsChange as EventListener)
})
</script>
<template>
<UiSidebarProvider>
<AdminSidebar />
<UiSidebarInset>
<header class="flex h-14 shrink-0 items-center gap-2 border-b px-4">
<UiSidebarTrigger class="-ml-1" />
<UiSeparator orientation="vertical" class="mr-2 h-4" />
<UiBreadcrumb>
<UiBreadcrumbList>
<template v-for="(crumb, index) in breadcrumbs" :key="crumb.path">
<UiBreadcrumbItem v-if="index < breadcrumbs.length - 1">
<UiBreadcrumbLink as-child>
<router-link :to="crumb.path">
{{ crumb.title }}
</router-link>
</UiBreadcrumbLink>
</UiBreadcrumbItem>
<UiBreadcrumbItem v-else>
<UiBreadcrumbPage>{{ crumb.title }}</UiBreadcrumbPage>
</UiBreadcrumbItem>
<UiBreadcrumbSeparator v-if="index < breadcrumbs.length - 1" />
</template>
</UiBreadcrumbList>
</UiBreadcrumb>
<div class="ml-auto flex items-center gap-2">
<LanguageChange />
<ToggleTheme />
<ThemePopover />
</div>
</header>
<main class="flex-1 overflow-auto p-4 md:p-6">
<router-view />
</main>
</UiSidebarInset>
</UiSidebarProvider>
</template>