improve(ui):优化用户端详情页返回方式

This commit is contained in:
chengqian committed 2026-09-09 14:34:01 +08:00
1 parent d1c8b40eca
commit 73db4465db
3 files changed
+98 -17

No files matched your search

+58 -2
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
import { ref, computed, onMounted, onBeforeUnmount, provide, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useTenantAuthStore } from '@/stores/tenant-auth'
import { useNotificationCount } from '@/composables/useNotificationCount'
@@ -85,6 +85,42 @@ const pageTitle = computed(() => {
return (leaf?.meta?.title as string) || navItems.value.find((i) => isActive(i.path))?.label || '仪表盘'
})
// 返回按钮注册机制(使用 provide/inject)
const backButtonConfig = ref<{
show: boolean
title: string
handler: () => void
} | null>(null)
// 提供给子页面的注册方法
function registerBackButton(config: { title: string; handler: () => void }) {
backButtonConfig.value = {
show: true,
title: config.title,
handler: config.handler,
}
}
// 提供给子页面的取消注册方法
function unregisterBackButton() {
backButtonConfig.value = null
}
// 通过 provide 暴露给子组件
provide('registerBackButton', registerBackButton)
provide('unregisterBackButton', unregisterBackButton)
// 监听路由变化,清空返回按钮配置
watch(() => route.path, () => {
backButtonConfig.value = null
})
const showBackButton = computed(() => backButtonConfig.value?.show ?? false)
const backButtonTitle = computed(() => backButtonConfig.value?.title ?? '返回')
function handleBack() {
backButtonConfig.value?.handler()
}
function isActive(path: string): boolean {
return activePath.value === path || activePath.value.startsWith(path + '/')
}
@@ -329,13 +365,33 @@ onBeforeUnmount(() => {
<div class="glass flex h-16 items-center justify-between rounded-2xl border border-white/70 px-3 shadow-glass-sm md:px-5">
<!-- Left: Mobile Menu + Title -->
<div class="flex min-w-0 items-center gap-3">
<!-- Mobile: 返回按钮或菜单按钮 -->
<button
v-if="showBackButton"
@click="handleBack"
class="btn-ghost btn-icon lg:hidden"
:title="backButtonTitle"
>
<Icon name="chevronLeft" size="md" />
</button>
<button
v-else
@click="toggleMobile"
class="btn-ghost btn-icon lg:hidden"
>
<Icon name="menu" size="md" />
</button>
<div class="hidden min-w-0 lg:block">
<!-- Desktop: 返回按钮 + 标题 -->
<div class="hidden min-w-0 lg:flex lg:items-center lg:gap-2">
<button
v-if="showBackButton"
@click="handleBack"
class="btn-ghost btn-icon flex-shrink-0 text-gray-500 hover:text-gray-700"
:title="backButtonTitle"
>
<Icon name="chevronLeft" size="md" />
</button>
<h1 class="truncate text-base font-semibold text-slate-800">{{ pageTitle }}</h1>
</div>
</div>
+20 -14
View File
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { formatNumber } from '@/utils/renderUtils'
import { ref, computed, onMounted } from 'vue'
import { ref, computed, onMounted, inject, onBeforeUnmount } from 'vue'
import { NInput, NCheckbox } from 'naive-ui'
import { useRouter, useRoute } from 'vue-router'
import { useTenantAuthStore } from '@/stores/tenant-auth'
@@ -17,6 +17,10 @@ const route = useRoute()
const authStore = useTenantAuthStore()
const teamEnabled = computed(() => !!authStore.tenant?.team_enabled)
// 注入返回按钮注册方法
const registerBackButton = inject<(config: { title: string; handler: () => void }) => void>('registerBackButton')
const unregisterBackButton = inject<() => void>('unregisterBackButton')
const memberId = computed(() => Number(route.params.id))
interface MemberInfo {
@@ -412,11 +416,15 @@ async function handleSaveModels() {
}
}
function goBack() {
router.push('/tenant/members')
}
onMounted(() => {
// 注册返回按钮
if (registerBackButton) {
registerBackButton({
title: '返回成员列表',
handler: () => router.push('/tenant/members'),
})
}
fetchMemberDetail()
fetchUsage()
fetchApiKeyCount()
@@ -424,20 +432,18 @@ onMounted(() => {
fetchAllModels()
fetchMemberModels()
})
onBeforeUnmount(() => {
// 取消注册返回按钮
if (unregisterBackButton) {
unregisterBackButton()
}
})
</script>
<template>
<TeamLockedBanner v-if="!teamEnabled" />
<div v-else class="space-y-8">
<!-- Back navigation -->
<button
@click="goBack"
class="btn btn-ghost btn-sm text-gray-500 hover:text-gray-700 -ml-2"
>
<Icon name="chevronLeft" size="sm" />
返回成员列表
</button>
<!-- Loading state -->
<div v-if="loading" class="flex justify-center py-12">
<div class="spinner h-8 w-8 border-primary-500"></div>
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, reactive, computed, onMounted, h } from 'vue'
import { ref, reactive, computed, onMounted, onBeforeUnmount, inject, h } from 'vue'
import type { DataTableColumns } from 'naive-ui'
import { NButton, NDropdown, NTag } from 'naive-ui'
import { renderBadge, formatDate, formatTokens, formatMs, tableScrollX } from '@/utils/renderUtils'
@@ -20,6 +20,10 @@ const route = useRoute()
const router = useRouter()
const projectId = computed(() => Number(route.params.id))
// 注入返回按钮注册方法
const registerBackButton = inject<(config: { title: string; handler: () => void }) => void>('registerBackButton')
const unregisterBackButton = inject<() => void>('unregisterBackButton')
// Project info
interface Project {
id: number
@@ -116,9 +120,24 @@ async function openScopeModal(keyId: number, keyName: string) {
}
onMounted(() => {
// 注册返回按钮
if (registerBackButton) {
registerBackButton({
title: '返回项目列表',
handler: () => router.push('/tenant/projects'),
})
}
fetchProject()
})
onBeforeUnmount(() => {
// 取消注册返回按钮
if (unregisterBackButton) {
unregisterBackButton()
}
})
// Usage stats
const usageStats = ref<any>(null)
const usageLoading = ref(false)