新增: 收藏展示名自定义与列表默认展开

- 收藏项支持重命名,displayName空值回退原名
- 悬停提示改两行,路径加来源连接名区分同名收藏
- 收藏夹区块默认展开
This commit is contained in:
lxy
2026-09-28 12:18:55 +08:00
parent e8ccb4280b
commit 15dedfc563
4 changed files with 116 additions and 6 deletions
@@ -79,7 +79,7 @@
@dragend="handleDragEnd"
>
<span class="sidebar-item-icon">{{ getFileIcon(fav) }}</span>
<span class="sidebar-item-name" :title="fav.name">{{ fav.name }}</span>
<span class="sidebar-item-name" :title="favoriteTooltip(fav)">{{ favoriteName(fav) }}</span>
<a-button
type="text"
size="mini"
@@ -91,6 +91,17 @@
<icon-pushpin :style="{ opacity: fav.pinnedAt ? 1 : 0.4 }" />
</template>
</a-button>
<a-button
type="text"
size="mini"
title="重命名收藏"
@click.stop="openRename(fav)"
class="sidebar-item-rename"
>
<template #icon>
<icon-edit />
</template>
</a-button>
<a-button
type="text"
size="mini"
@@ -131,6 +142,25 @@
</div>
</transition>
<!-- 重命名收藏弹窗 -->
<a-modal
:visible="renameVisible"
title="重命名收藏"
:width="420"
:mask-closable="false"
unmount-on-close
@cancel="renameVisible = false"
@before-ok="handleRenameOk"
>
<a-input
ref="renameInputRef"
v-model="renameValue"
:placeholder="renameOriginal"
:max-length="255"
allow-clear
/>
</a-modal>
<!-- 更多操作子菜单 — Teleport 到 body -->
<Teleport to="body">
<div v-if="moreOpenId" class="sidebar-popup more-menu" :style="moreMenuStyle" @click.stop>
@@ -165,12 +195,12 @@
</template>
<script setup lang="ts">
import { ref, computed, shallowRef, onMounted, onUnmounted } from 'vue'
import { ref, computed, shallowRef, watch, onMounted, onUnmounted } from 'vue'
import type { SidebarConfig, FavoriteFile } from '@/types/file-system'
import type { SystemInfo } from '@/api/connection-manager'
import { connectionManager } from '@/api/connection-manager'
import { useConfigStore } from '@/stores/config'
import { IconStar, IconClose, IconPushpin, IconDown, IconRight, IconStorage, IconComputer, IconDesktop, IconPlus, IconCheckCircle } from '@arco-design/web-vue/es/icon'
import { IconStar, IconClose, IconPushpin, IconEdit, IconDown, IconRight, IconStorage, IconComputer, IconDesktop, IconPlus, IconCheckCircle } from '@arco-design/web-vue/es/icon'
import { getFileIcon } from '@/utils/fileUtils'
const configStore = useConfigStore()
@@ -182,9 +212,9 @@ interface Props {
const props = defineProps<Props>()
// 折叠状态(组件内部,不污染父组件)
// 折叠状态(组件内部,不污染父组件);收藏夹默认展开,用户手动折叠后会话内保持
const serverCollapsed = ref(false)
const favCollapsed = ref(true)
const favCollapsed = ref(false)
const helpCollapsed = ref(true)
// 侧边栏区块可见性(从配置读取)
@@ -343,6 +373,7 @@ interface Emits {
(e: 'openFavorite', file: FavoriteFile): void
(e: 'removeFavorite', path: string): void
(e: 'togglePin', path: string): void
(e: 'renameFavorite', path: string, displayName: string): void
(e: 'longPressStart', event: MouseEvent | TouchEvent, index: number): void
(e: 'longPressCancel'): void
(e: 'dragStart', event: DragEvent, index: number): void
@@ -368,6 +399,57 @@ const handleTogglePin = (file: FavoriteFile) => {
emit('togglePin', file.path)
}
// 重命名收藏弹窗(预填展示名,placeholder 显示原名)
const renameVisible = ref(false)
const renameValue = ref('')
const renameTarget = ref<FavoriteFile | null>(null)
const renameInputRef = ref()
const renameOriginal = computed(() => renameTarget.value?.name ?? '')
const openRename = (fav: FavoriteFile) => {
renameTarget.value = fav
renameValue.value = fav.displayName || ''
renameVisible.value = true
}
watch(renameVisible, (val) => {
if (!val) return
setTimeout(() => {
renameInputRef.value?.focus()
}, 100)
})
const handleRenameOk = (done: (closed?: boolean) => void) => {
if (!renameTarget.value) {
done(false)
return
}
// 空值表示清除展示名,回退显示原名
emit('renameFavorite', renameTarget.value.path, renameValue.value.trim())
done()
}
// 展示名优先,未设置回退原名
const favoriteName = (fav: FavoriteFile): string => fav.displayName || fav.name
// 悬停提示两行:完整路径 + 来源连接名
const favoriteTooltip = (fav: FavoriteFile): string => {
const source = favoriteSourceName(fav)
return source ? `${fav.path}\n来源:${source}` : fav.path
}
function favoriteSourceName(fav: FavoriteFile): string {
if (fav.profileId) {
return connectionManager.profiles.find(p => p.id === fav.profileId)?.name ?? ''
}
// 旧数据无 profileId,按路径格式推断连接(与加载迁移逻辑一致)
const isLocalPath = /^[A-Za-z]:/.test(fav.path) || fav.path.startsWith('\\\\')
if (isLocalPath) {
return connectionManager.profiles.find(p => p.type === 'local')?.name ?? '本地'
}
return connectionManager.profiles.find(p => p.type !== 'local')?.name ?? ''
}
const handleLongPressStart = (event: MouseEvent | TouchEvent, index: number) => {
emit('longPressStart', event, index)
}
@@ -844,6 +926,15 @@ function handleDelete(p: { id: string; name: string }) {
opacity: 1;
}
.sidebar-item-rename {
opacity: 0;
transition: opacity 0.2s;
}
.sidebar-item:hover .sidebar-item-rename {
opacity: 1;
}
/* 空状态 */
.sidebar-empty {
display: flex;
@@ -173,6 +173,17 @@ export function useFavorites() {
return !!fav?.pinnedAt
}
/**
* 设置收藏项展示名(空串表示清除,显示时回退原名)
*/
const renameFavorite = (path: string, displayName: string) => {
const normalizedPath = normalizePath(path)
const fav = favorites.value.find(f => normalizePath(f.path) === normalizedPath)
if (!fav) return
fav.displayName = displayName
saveFavorites()
}
/**
* 更新收藏项路径(重命名时使用,保留置顶状态和添加时间)
*/
@@ -268,6 +279,7 @@ export function useFavorites() {
isFavorite,
togglePin,
isPinned,
renameFavorite,
updateFavoritePath,
onLongPressStart,
+6 -1
View File
@@ -9,6 +9,7 @@
@open-favorite="handleOpenFavorite"
@remove-favorite="handleRemoveFavorite"
@toggle-pin="handleToggleFavPin"
@rename-favorite="handleRenameFavorite"
@long-press-start="handleLongPressStart"
@long-press-cancel="handleLongPressCancel"
@drag-start="handleDragStart"
@@ -305,7 +306,7 @@ const { clipboard, clipboardEmpty, setCopy, setCut, clear: clearClipboard } = us
const transferStore = useTransferStore()
// 收藏夹
const { favorites, draggingState, toggleFavorite: toggleFav, removeFavorite: removeFav, isFavorite, togglePin, updateFavoritePath, onLongPressStart, onLongPressCancel, onDragStart, onDragOver, onDrop, onDragEnd } = useFavorites()
const { favorites, draggingState, toggleFavorite: toggleFav, removeFavorite: removeFav, isFavorite, togglePin, renameFavorite: renameFav, updateFavoritePath, onLongPressStart, onLongPressCancel, onDragStart, onDragOver, onDrop, onDragEnd } = useFavorites()
// 路径导航
const { filePath, history, navigate, back, forward, onPathSelect, onPathEnter, browseDirectory, getParentPath, getProfileIdForPath } =
@@ -691,6 +692,10 @@ const handleToggleFavPin = (path: string) => {
togglePin(path)
}
const handleRenameFavorite = (path: string, displayName: string) => {
renameFav(path, displayName)
}
const handleLongPressStart = (event: MouseEvent | TouchEvent, index: number) => {
onLongPressStart(event, index)
}
+2
View File
@@ -33,6 +33,8 @@ export interface FavoriteFile extends FileItem {
pinnedAt?: number
/** 关联的连接配置 ID,用于打开收藏时自动切换连接 */
profileId?: string
/** 展示名,空串或缺省表示未设置(显示时回退原名) */
displayName?: string
}
/**