feat: 为文件视图添加侧边栏标签页
This commit is contained in:
@@ -3,18 +3,21 @@ import { ref, computed } from 'vue'
|
||||
import { RouterLink, useRoute, useRouter } from 'vue-router'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
import { useConnectionsStore } from '../stores/connections'
|
||||
import { useSftpTabsStore } from '../stores/sftpTabs'
|
||||
import { useTerminalTabsStore } from '../stores/terminalTabs'
|
||||
import TerminalWorkspaceView from '../views/TerminalWorkspaceView.vue'
|
||||
import { ArrowLeftRight, Server, LogOut, Menu, X, Terminal } from 'lucide-vue-next'
|
||||
import { ArrowLeftRight, Server, LogOut, Menu, X, Terminal, FolderOpen } from 'lucide-vue-next'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const authStore = useAuthStore()
|
||||
const connectionsStore = useConnectionsStore()
|
||||
const tabsStore = useTerminalTabsStore()
|
||||
const authStore = useAuthStore()
|
||||
const connectionsStore = useConnectionsStore()
|
||||
const sftpTabsStore = useSftpTabsStore()
|
||||
const tabsStore = useTerminalTabsStore()
|
||||
const sidebarOpen = ref(false)
|
||||
|
||||
const terminalTabs = computed(() => tabsStore.tabs)
|
||||
const sftpTabs = computed(() => sftpTabsStore.tabs)
|
||||
const showTerminalWorkspace = computed(() => route.path === '/terminal')
|
||||
const keepTerminalWorkspaceMounted = computed(() => showTerminalWorkspace.value || terminalTabs.value.length > 0)
|
||||
|
||||
@@ -30,10 +33,39 @@ function handleTabClick(tabId: string) {
|
||||
closeSidebar()
|
||||
}
|
||||
|
||||
function handleTabClose(tabId: string, event: Event) {
|
||||
event.stopPropagation()
|
||||
tabsStore.close(tabId)
|
||||
}
|
||||
function handleTabClose(tabId: string, event: Event) {
|
||||
event.stopPropagation()
|
||||
tabsStore.close(tabId)
|
||||
}
|
||||
|
||||
function isCurrentSftpRoute(connectionId: number) {
|
||||
if (route.name !== 'Sftp') return false
|
||||
|
||||
const routeParamId = Array.isArray(route.params.id) ? route.params.id[0] : route.params.id
|
||||
return Number(routeParamId) === connectionId
|
||||
}
|
||||
|
||||
function handleSftpTabClick(tabId: string, connectionId: number) {
|
||||
sftpTabsStore.activate(tabId)
|
||||
router.push(`/sftp/${connectionId}`)
|
||||
closeSidebar()
|
||||
}
|
||||
|
||||
function handleSftpTabClose(tabId: string, connectionId: number, event: Event) {
|
||||
event.stopPropagation()
|
||||
|
||||
const shouldNavigate = isCurrentSftpRoute(connectionId)
|
||||
sftpTabsStore.close(tabId)
|
||||
|
||||
if (!shouldNavigate) return
|
||||
|
||||
if (sftpTabsStore.activeTab) {
|
||||
router.push(`/sftp/${sftpTabsStore.activeTab.connectionId}`)
|
||||
return
|
||||
}
|
||||
|
||||
router.push('/connections')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -80,11 +112,11 @@ function handleTabClose(tabId: string, event: Event) {
|
||||
</RouterLink>
|
||||
|
||||
<!-- 终端标签区域 -->
|
||||
<div v-if="terminalTabs.length > 0" class="pt-4 mt-4 border-t border-slate-700">
|
||||
<div class="flex items-center gap-2 px-3 py-2 text-xs font-semibold text-slate-500 uppercase tracking-wider">
|
||||
<Terminal class="w-4 h-4" aria-hidden="true" />
|
||||
<span>终端</span>
|
||||
</div>
|
||||
<div v-if="terminalTabs.length > 0" class="pt-4 mt-4 border-t border-slate-700">
|
||||
<div class="flex items-center gap-2 px-3 py-2 text-xs font-semibold text-slate-500 uppercase tracking-wider">
|
||||
<Terminal class="w-4 h-4" aria-hidden="true" />
|
||||
<span>终端</span>
|
||||
</div>
|
||||
<div class="space-y-1 mt-2">
|
||||
<button
|
||||
v-for="tab in terminalTabs"
|
||||
@@ -101,9 +133,41 @@ function handleTabClose(tabId: string, event: Event) {
|
||||
>
|
||||
<X class="w-3 h-3" aria-hidden="true" />
|
||||
</button>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="sftpTabs.length > 0" class="pt-4 mt-4 border-t border-slate-700">
|
||||
<div class="flex items-center gap-2 px-3 py-2 text-xs font-semibold text-slate-500 uppercase tracking-wider">
|
||||
<FolderOpen class="w-4 h-4" aria-hidden="true" />
|
||||
<span>文件</span>
|
||||
</div>
|
||||
<div class="space-y-1 mt-2">
|
||||
<div
|
||||
v-for="tab in sftpTabs"
|
||||
:key="tab.id"
|
||||
class="w-full flex items-center justify-between gap-2 px-3 py-2.5 rounded-lg text-slate-300 hover:bg-slate-700 hover:text-slate-100 transition-colors duration-200 min-h-[44px] group focus-within:outline-none focus-within:ring-2 focus-within:ring-cyan-500 focus-within:ring-inset"
|
||||
:class="{ 'bg-slate-700 text-cyan-400': tab.active && route.path === `/sftp/${tab.connectionId}` }"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
@click="handleSftpTabClick(tab.id, tab.connectionId)"
|
||||
class="flex-1 min-w-0 text-left cursor-pointer focus:outline-none"
|
||||
:aria-label="`打开文件标签 ${tab.title}`"
|
||||
>
|
||||
<span class="truncate text-sm block">{{ tab.title }}</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="(e) => handleSftpTabClose(tab.id, tab.connectionId, e)"
|
||||
class="p-1 rounded opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 focus:opacity-100 hover:bg-slate-600 transition-opacity duration-200 transition-colors flex-shrink-0 focus:outline-none focus:ring-2 focus:ring-cyan-500"
|
||||
aria-label="关闭文件标签"
|
||||
>
|
||||
<X class="w-3 h-3" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="p-4 border-t border-slate-700">
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user