From 51f8639a3a26fdd010f371df8f0b0e7c0fa8db56 Mon Sep 17 00:00:00 2001 From: wangheng Date: Wed, 23 Sep 2026 13:03:16 +0800 Subject: [PATCH] =?UTF-8?q?feat(admin):=20=E7=AE=A1=E7=90=86=E7=AB=AF?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E5=A4=9A=E6=A0=87=E7=AD=BE=E5=AF=BC=E8=88=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 菜单点击打开标签页,标签可关闭,关闭当前标签自动切换相邻标签 - 页面面板 v-show 保活,切换标签保留筛选/分页/选中状态 - 激活标签仍刷新数据,登录/退出重置标签 - 补充多标签契约测试 --- xiaoqu-agent-service/front/src/App.vue | 48 +++++++++++++++++-- xiaoqu-agent-service/front/src/style.css | 6 +++ .../front/tests/source-contract.test.mjs | 13 +++++ 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/xiaoqu-agent-service/front/src/App.vue b/xiaoqu-agent-service/front/src/App.vue index 9217cc1..f1ee644 100644 --- a/xiaoqu-agent-service/front/src/App.vue +++ b/xiaoqu-agent-service/front/src/App.vue @@ -13,6 +13,7 @@ const menus = [ const loggedIn = ref(false) const busy = ref(false) const current = ref('tokens') +const openTabs = ref(['tokens']) const session = reactive({ name: '', csrfToken: '' }) const loginForm = reactive({ account: '', password: '' }) const users = ref([]) @@ -45,6 +46,9 @@ const scopeToolsKeyword = ref('') const scopeToolsOnlySelected = ref(false) const title = computed(() => menus.find(item => item.id === current.value)?.label || '') +const openedTabItems = computed(() => openTabs.value + .map(id => menus.find(item => item.id === id)) + .filter(Boolean)) const categories = computed(() => [...new Set(allTools.value.map(tool => tool.category))]) const groupedTools = computed(() => allTools.value.reduce((groups, tool) => { const category = tool.category || '其他' @@ -109,6 +113,7 @@ async function login() { const data = await api('/auth/login', { method: 'POST', body: JSON.stringify(loginForm) }) Object.assign(session, data); setCsrf(data.csrfToken); loggedIn.value = true loginForm.password = '' + openTabs.value = ['tokens']; current.value = 'tokens' await loadUsers() }).catch(() => {}) } @@ -116,9 +121,11 @@ async function login() { async function logout() { await run(() => api('/auth/logout', { method: 'POST' })).catch(() => {}) loggedIn.value = false; setCsrf('') + openTabs.value = ['tokens']; current.value = 'tokens' } async function switchMenu(id) { + if (!openTabs.value.includes(id)) openTabs.value = [...openTabs.value, id] current.value = id if (id === 'tokens') await loadUsers() if (id === 'clients') await loadOpenClients() @@ -127,6 +134,16 @@ async function switchMenu(id) { if (id === 'audit') await Promise.all([loadAudit(), loadAllTools()]) } +async function closeTab(id) { + const index = openTabs.value.indexOf(id) + if (index === -1) return + openTabs.value = openTabs.value.filter(tab => tab !== id) + if (current.value !== id) return + const next = openTabs.value[Math.min(index, openTabs.value.length - 1)] + current.value = next || '' + if (next) await switchMenu(next) +} + async function loadUsers(resetPage = false) { if (resetPage) userPage.page = 1 const data = await api(`/users${qs({ keyword: userKeyword.value, page: userPage.page, pageSize: userPage.pageSize })}`) @@ -376,8 +393,20 @@ onMounted(async () => { 退出 +
-