Private
Public Access
1
0

优化:Sidebar服务器状态区块+布局重构+连接对话框优化+gitignore更新

This commit is contained in:
2026-05-01 21:53:37 +08:00
parent f54bf1c28d
commit 3e1a540b83
7 changed files with 178 additions and 46 deletions

2
.gitignore vendored
View File

@@ -4,3 +4,5 @@ frontend/dist
frontend/node_modules
build/linux/appimage/build
build/windows/nsis/MicrosoftEdgeWebview2Setup.exe
.idea/
.claude/

View File

@@ -1,23 +1,21 @@
<template>
<a-modal :visible="props.visible" :title="editingId ? '编辑连接' : '添加服务器'" unmount-on-close @cancel="emit('update:visible', false)" @before-ok="handleOk" :ok-loading="submitting">
<div style="display: flex; flex-direction: column; gap: 16px; max-width: 400px">
<div>
<div style="margin-bottom: 4px; font-size: 14px">名称</div>
<a-input v-model="form.name" placeholder="如:生产服务器" />
<div style="display: flex; flex-direction: column; gap: 10px; max-width: 400px">
<div style="display: flex; align-items: center; gap: 8px">
<label style="font-size: 13px; width: 36px; flex-shrink: 0">名称</label>
<a-input v-model="form.name" placeholder="如:生产服务器" style="flex: 1" />
</div>
<div>
<div style="margin-bottom: 4px; font-size: 14px">地址</div>
<a-input v-model="form.host" placeholder="192.168.1.100" />
<div style="display: flex; align-items: center; gap: 8px">
<label style="font-size: 13px; width: 36px; flex-shrink: 0">地址</label>
<a-input v-model="form.host" placeholder="192.168.1.100" style="flex: 1" />
<a-input-number v-model="form.port" :min="1" :max="65535" placeholder="端口" style="width: 90px" hide-button />
</div>
<div>
<div style="margin-bottom: 4px; font-size: 14px">端口</div>
<a-input-number v-model="form.port" :min="1" :max="65535" placeholder="9876" style="width: 100%" />
</div>
<div>
<div style="margin-bottom: 4px; font-size: 14px">
Token <span style="color: var(--color-text-3); font-size: 12px">API 认证令牌与服务器配置一致</span>
<div style="display: flex; align-items: center; gap: 8px">
<label style="font-size: 13px; width: 36px; flex-shrink: 0">Token</label>
<div style="flex: 1">
<a-input v-model="form.token" type="password" placeholder="留空则不认证" allow-clear />
<div style="font-size: 11px; color: var(--color-text-3); margin-top: 2px">API 认证令牌与服务器配置一致</div>
</div>
<a-input v-model="form.token" type="password" placeholder="留空则不认证" allow-clear />
</div>
</div>
</a-modal>

View File

@@ -154,12 +154,12 @@ function handleDelete(p: { id: string; name: string }) {
flex-shrink: 0;
}
.dot.connected { background: rgb(var(--green-6)); }
.dot.connecting { background: #f5a623; animation: pulse 1.5s infinite; }
.dot.connected { background: var(--color-success-6); }
.dot.connecting { background: var(--color-warning-6); animation: pulse 1.5s infinite; }
.dot.disconnected { background: var(--color-danger-6); }
.dot.error { background: var(--color-danger-6); }
.dot.local { background: var(--color-text-3); }
.dot.remote { background: #165dff; }
.dot.remote { background: var(--color-primary-6); }
.label {
max-width: 70px;

View File

@@ -1,6 +1,49 @@
<template>
<transition name="slide">
<div v-show="config.visible" class="sidebar">
<!-- 服务器区块 -->
<div class="sidebar-section">
<div class="section-header" @click="serverCollapsed = !serverCollapsed">
<span class="section-title">🖥 服务器</span>
<a-tag :color="statusTagColor" size="small">{{ statusLabel }}</a-tag>
<icon-down v-if="!serverCollapsed" class="section-toggle" />
<icon-right v-else class="section-toggle" />
</div>
<div class="section-content server-content" :class="{ collapsed: serverCollapsed }">
<div class="server-info">
<div class="server-row">
<span class="server-label">模式</span>
<a-tag :color="isRemote ? 'blue' : 'green'" size="small">{{ isRemote ? '远程' : '本地' }}</a-tag>
</div>
<div v-if="activeProfile" class="server-row">
<span class="server-label">服务器</span>
<span class="server-val">{{ activeProfile.name }}</span>
</div>
</div>
<div class="server-actions">
<a-button
v-if="!isRemote"
type="outline"
size="mini"
long
@click.stop="handleConnectRemote"
>
连接远程
</a-button>
<a-button
v-else
type="outline"
status="danger"
size="mini"
long
@click.stop="handleDisconnect"
>
断开连接
</a-button>
</div>
</div>
</div>
<!-- 收藏夹区块 -->
<div class="sidebar-section">
<div class="section-header" @click="favCollapsed = !favCollapsed">
@@ -9,7 +52,7 @@
<icon-down v-if="!favCollapsed" class="section-toggle" />
<icon-right v-else class="section-toggle" />
</div>
<div class="section-content" :class="{ collapsed: favCollapsed }">
<div class="section-content fav-content" :class="{ collapsed: favCollapsed }">
<div
v-for="(fav, index) in config.favoriteFiles"
:key="fav.path"
@@ -87,6 +130,9 @@
<script setup lang="ts">
import { ref, computed } from 'vue'
import type { SidebarConfig, FavoriteFile } from '@/types/file-system'
import { connectionManager } from '@/api/connection-manager'
import { IconStar, IconClose, IconPushpin, IconDown, IconRight } from '@arco-design/web-vue/es/icon'
import { getFileIcon } from '@/utils/fileUtils'
// Props
interface Props {
@@ -96,9 +142,32 @@ interface Props {
const props = defineProps<Props>()
// 折叠状态(组件内部,不污染父组件)
const serverCollapsed = ref(false)
const favCollapsed = ref(false)
const helpCollapsed = ref(false)
// 服务器响应式状态connectionManager 非响应式,需手动桥接)
const connState = ref(connectionManager.state)
const isRemote = ref(connectionManager.isRemote())
const activeProfile = ref(connectionManager.activeProfile)
connectionManager.onStateChange(() => {
connState.value = connectionManager.state
isRemote.value = connectionManager.isRemote()
activeProfile.value = connectionManager.activeProfile
})
const statusMap: Record<string, string> = {
connecting: '连接中...',
connected: '已连接',
error: '异常',
}
const statusLabel = computed(() => statusMap[connState.value] || connState.value)
const statusTagColor = computed(() => {
const map: Record<string, string> = { connecting: 'orangered', connected: 'blue', error: 'red' }
return map[connState.value] || 'gray'
})
// 计算第一个和最后一个置顶项的索引
const pinnedIndices = computed(() => {
return props.config.favoriteFiles
@@ -129,14 +198,11 @@ interface Emits {
(e: 'dragOver', event: DragEvent): void
(e: 'drop', event: DragEvent, targetIndex: number): void
(e: 'dragEnd'): void
(e: 'openConnectionDialog'): void
}
const emit = defineEmits<Emits>()
// 图标导入
import { IconStar, IconClose, IconPushpin, IconDown, IconRight } from '@arco-design/web-vue/es/icon'
import { getFileIcon } from '@/utils/fileUtils'
// 事件处理
const handleOpenFavorite = (file: FavoriteFile) => {
emit('openFavorite', file)
@@ -173,6 +239,20 @@ const handleDrop = (event: DragEvent, targetIndex: number) => {
const handleDragEnd = () => {
emit('dragEnd')
}
// 服务器操作
const handleConnectRemote = () => {
const remote = connectionManager.profiles.find(p => p.type === 'remote')
if (remote) {
connectionManager.connect(remote.id)
} else {
emit('openConnectionDialog')
}
}
const handleDisconnect = () => {
connectionManager.disconnect()
}
</script>
<style scoped>
@@ -248,7 +328,7 @@ const handleDragEnd = () => {
}
/* 收藏夹内容 - 内部独立滚动 */
.section-content:not(.help-content) {
.fav-content {
flex: 1;
min-height: 0;
overflow-y: auto;
@@ -283,6 +363,40 @@ const handleDragEnd = () => {
color: var(--color-text-3);
}
/* 服务器内容 */
.server-content {
padding: 8px 12px;
background: var(--color-fill-1);
border-radius: 0 0 6px 6px;
}
.server-info {
display: flex;
flex-direction: column;
gap: 6px;
margin-bottom: 8px;
}
.server-row {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
}
.server-label {
color: var(--color-text-3);
min-width: 42px;
}
.server-val {
color: var(--color-text-1);
font-weight: 500;
}
.server-actions :deep(.arco-btn) {
font-size: 12px;
}
/* 收藏项 */
.sidebar-item {
display: flex;

View File

@@ -148,6 +148,7 @@ import ConnectionDialog from './ConnectionDialog.vue'
// Props
interface Props {
config: ToolbarConfig
openConnectionDialog?: boolean
}
const props = defineProps<Props>()
@@ -170,6 +171,9 @@ const emit = defineEmits<Emits>()
// 连接对话框
const showConnectionDialog = ref(false)
const connectionDialogRef = ref<InstanceType<typeof ConnectionDialog>>()
watch(() => props.openConnectionDialog, (v) => { if (v > 0) showConnectionDialog.value = true })
const onConnectionChanged = async (_id: string) => {
emit('connectionChanged')
}

View File

@@ -1,24 +1,8 @@
<template>
<div class="file-system-container">
<!-- 顶部工具栏 -->
<Toolbar
ref="toolbarRef"
:config="toolbarConfig"
@update:file-path="handleFilePathUpdate"
@update:show-sidebar="handleSidebarToggle"
@refresh="handleRefresh"
@exit-zip="handleExitZip"
@go-to-path="handleGoToPath"
@open-file="handleOpenFile"
@navigate-to-zip-directory="handleNavigateToZipDirectory"
@update:search-keyword="handleSearchKeywordUpdate"
@show-message="handleShowMessage"
@connection-changed="handleConnectionChanged"
/>
<!-- 主内容区 -->
<!-- 主内容区左侧边栏 + 右侧工作区 -->
<div class="main-content">
<!-- 左侧收藏夹侧边栏 -->
<!-- 左侧收藏夹侧边栏全高 -->
<Sidebar
v-if="showSidebar"
:config="sidebarConfig"
@@ -31,10 +15,30 @@
@drag-over="handleDragOver"
@drop="handleDrop"
@drag-end="handleDragEnd"
@open-connection-dialog="triggerConnectionDialog++"
/>
<!-- 文件列表和编辑器区域 -->
<div ref="workspaceRef" class="file-workspace">
<!-- 右侧工作区面包屑工具栏 + 文件列表/编辑器 -->
<div class="workspace-area">
<!-- 面包屑导航工具栏 -->
<Toolbar
ref="toolbarRef"
:config="toolbarConfig"
:open-connection-dialog="triggerConnectionDialog"
@update:file-path="handleFilePathUpdate"
@update:show-sidebar="handleSidebarToggle"
@refresh="handleRefresh"
@exit-zip="handleExitZip"
@go-to-path="handleGoToPath"
@open-file="handleOpenFile"
@navigate-to-zip-directory="handleNavigateToZipDirectory"
@update:search-keyword="handleSearchKeywordUpdate"
@show-message="handleShowMessage"
@connection-changed="handleConnectionChanged"
/>
<!-- 文件列表和编辑器区域 -->
<div ref="workspaceRef" class="file-workspace">
<!-- 文件列表面板 -->
<FileListPanel
:config="fileListPanelConfig"
@@ -72,6 +76,7 @@
@open-local-file="handleOpenLocalFile"
/>
</div>
</div>
</div>
<!-- 右键菜单 -->
@@ -156,6 +161,7 @@ const fileList = ref<FileItem[]>([])
const fileLoading = ref(false)
const selectedFileItem = ref<FileItem | null>(null)
const toolbarRef = ref<InstanceType<typeof import('./components/Toolbar.vue').default> | null>(null)
const triggerConnectionDialog = ref(0)
// 排序状态(带 localStorage 持久化)
const SORT_STORAGE_KEY = STORAGE_KEYS.FILESYSTEM.SORT
@@ -1555,6 +1561,14 @@ watch(() => themeStore.isDark, async () => {
overflow: hidden;
}
.workspace-area {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
min-width: 0;
}
.file-workspace {
flex: 1;
display: flex;

View File

@@ -77,7 +77,7 @@ func main() {
// TODO: 替换为 OnDomReady 回调,当前 alpha.80 可能未稳定支持
go func() {
time.Sleep(2 * time.Second)
_ = window.OpenDevTools()
window.OpenDevTools()
}()
if err := wailsApp.Run(); err != nil {