新增: 收藏展示名自定义与列表默认展开
- 收藏项支持重命名,displayName空值回退原名 - 悬停提示改两行,路径加来源连接名区分同名收藏 - 收藏夹区块默认展开
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ export interface FavoriteFile extends FileItem {
|
||||
pinnedAt?: number
|
||||
/** 关联的连接配置 ID,用于打开收藏时自动切换连接 */
|
||||
profileId?: string
|
||||
/** 展示名,空串或缺省表示未设置(显示时回退原名) */
|
||||
displayName?: string
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user