新增: 收藏展示名自定义与列表默认展开
- 收藏项支持重命名,displayName空值回退原名 - 悬停提示改两行,路径加来源连接名区分同名收藏 - 收藏夹区块默认展开
This commit is contained in:
@@ -79,7 +79,7 @@
|
|||||||
@dragend="handleDragEnd"
|
@dragend="handleDragEnd"
|
||||||
>
|
>
|
||||||
<span class="sidebar-item-icon">{{ getFileIcon(fav) }}</span>
|
<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
|
<a-button
|
||||||
type="text"
|
type="text"
|
||||||
size="mini"
|
size="mini"
|
||||||
@@ -91,6 +91,17 @@
|
|||||||
<icon-pushpin :style="{ opacity: fav.pinnedAt ? 1 : 0.4 }" />
|
<icon-pushpin :style="{ opacity: fav.pinnedAt ? 1 : 0.4 }" />
|
||||||
</template>
|
</template>
|
||||||
</a-button>
|
</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
|
<a-button
|
||||||
type="text"
|
type="text"
|
||||||
size="mini"
|
size="mini"
|
||||||
@@ -131,6 +142,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</transition>
|
</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 到 body -->
|
||||||
<Teleport to="body">
|
<Teleport to="body">
|
||||||
<div v-if="moreOpenId" class="sidebar-popup more-menu" :style="moreMenuStyle" @click.stop>
|
<div v-if="moreOpenId" class="sidebar-popup more-menu" :style="moreMenuStyle" @click.stop>
|
||||||
@@ -165,12 +195,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<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 { SidebarConfig, FavoriteFile } from '@/types/file-system'
|
||||||
import type { SystemInfo } from '@/api/connection-manager'
|
import type { SystemInfo } from '@/api/connection-manager'
|
||||||
import { connectionManager } from '@/api/connection-manager'
|
import { connectionManager } from '@/api/connection-manager'
|
||||||
import { useConfigStore } from '@/stores/config'
|
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'
|
import { getFileIcon } from '@/utils/fileUtils'
|
||||||
|
|
||||||
const configStore = useConfigStore()
|
const configStore = useConfigStore()
|
||||||
@@ -182,9 +212,9 @@ interface Props {
|
|||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
// 折叠状态(组件内部,不污染父组件)
|
// 折叠状态(组件内部,不污染父组件);收藏夹默认展开,用户手动折叠后会话内保持
|
||||||
const serverCollapsed = ref(false)
|
const serverCollapsed = ref(false)
|
||||||
const favCollapsed = ref(true)
|
const favCollapsed = ref(false)
|
||||||
const helpCollapsed = ref(true)
|
const helpCollapsed = ref(true)
|
||||||
|
|
||||||
// 侧边栏区块可见性(从配置读取)
|
// 侧边栏区块可见性(从配置读取)
|
||||||
@@ -343,6 +373,7 @@ interface Emits {
|
|||||||
(e: 'openFavorite', file: FavoriteFile): void
|
(e: 'openFavorite', file: FavoriteFile): void
|
||||||
(e: 'removeFavorite', path: string): void
|
(e: 'removeFavorite', path: string): void
|
||||||
(e: 'togglePin', path: string): void
|
(e: 'togglePin', path: string): void
|
||||||
|
(e: 'renameFavorite', path: string, displayName: string): void
|
||||||
(e: 'longPressStart', event: MouseEvent | TouchEvent, index: number): void
|
(e: 'longPressStart', event: MouseEvent | TouchEvent, index: number): void
|
||||||
(e: 'longPressCancel'): void
|
(e: 'longPressCancel'): void
|
||||||
(e: 'dragStart', event: DragEvent, index: number): void
|
(e: 'dragStart', event: DragEvent, index: number): void
|
||||||
@@ -368,6 +399,57 @@ const handleTogglePin = (file: FavoriteFile) => {
|
|||||||
emit('togglePin', file.path)
|
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) => {
|
const handleLongPressStart = (event: MouseEvent | TouchEvent, index: number) => {
|
||||||
emit('longPressStart', event, index)
|
emit('longPressStart', event, index)
|
||||||
}
|
}
|
||||||
@@ -844,6 +926,15 @@ function handleDelete(p: { id: string; name: string }) {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-item-rename {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-item:hover .sidebar-item-rename {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* 空状态 */
|
/* 空状态 */
|
||||||
.sidebar-empty {
|
.sidebar-empty {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -173,6 +173,17 @@ export function useFavorites() {
|
|||||||
return !!fav?.pinnedAt
|
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,
|
isFavorite,
|
||||||
togglePin,
|
togglePin,
|
||||||
isPinned,
|
isPinned,
|
||||||
|
renameFavorite,
|
||||||
updateFavoritePath,
|
updateFavoritePath,
|
||||||
|
|
||||||
onLongPressStart,
|
onLongPressStart,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
@open-favorite="handleOpenFavorite"
|
@open-favorite="handleOpenFavorite"
|
||||||
@remove-favorite="handleRemoveFavorite"
|
@remove-favorite="handleRemoveFavorite"
|
||||||
@toggle-pin="handleToggleFavPin"
|
@toggle-pin="handleToggleFavPin"
|
||||||
|
@rename-favorite="handleRenameFavorite"
|
||||||
@long-press-start="handleLongPressStart"
|
@long-press-start="handleLongPressStart"
|
||||||
@long-press-cancel="handleLongPressCancel"
|
@long-press-cancel="handleLongPressCancel"
|
||||||
@drag-start="handleDragStart"
|
@drag-start="handleDragStart"
|
||||||
@@ -305,7 +306,7 @@ const { clipboard, clipboardEmpty, setCopy, setCut, clear: clearClipboard } = us
|
|||||||
const transferStore = useTransferStore()
|
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 } =
|
const { filePath, history, navigate, back, forward, onPathSelect, onPathEnter, browseDirectory, getParentPath, getProfileIdForPath } =
|
||||||
@@ -691,6 +692,10 @@ const handleToggleFavPin = (path: string) => {
|
|||||||
togglePin(path)
|
togglePin(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleRenameFavorite = (path: string, displayName: string) => {
|
||||||
|
renameFav(path, displayName)
|
||||||
|
}
|
||||||
|
|
||||||
const handleLongPressStart = (event: MouseEvent | TouchEvent, index: number) => {
|
const handleLongPressStart = (event: MouseEvent | TouchEvent, index: number) => {
|
||||||
onLongPressStart(event, index)
|
onLongPressStart(event, index)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ export interface FavoriteFile extends FileItem {
|
|||||||
pinnedAt?: number
|
pinnedAt?: number
|
||||||
/** 关联的连接配置 ID,用于打开收藏时自动切换连接 */
|
/** 关联的连接配置 ID,用于打开收藏时自动切换连接 */
|
||||||
profileId?: string
|
profileId?: string
|
||||||
|
/** 展示名,空串或缺省表示未设置(显示时回退原名) */
|
||||||
|
displayName?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user