feat: 添加标签管理模态框

This commit is contained in:
Baobhan Sith
2025-05-11 11:20:26 +08:00
parent 1eb1efde72
commit 598df938bf
40 changed files with 634 additions and 170 deletions
@@ -25,21 +25,21 @@ export interface UseFileManagerContextMenuOptions {
currentPath: Ref<string>;
isConnected: Ref<boolean>;
isSftpReady: Ref<boolean>;
clipboardState: Ref<Readonly<ClipboardState>>; // +++ 新增:剪贴板状态 +++
clipboardState: Ref<Readonly<ClipboardState>>; // +++ 剪贴板状态 +++
t: ReturnType<typeof useI18n>['t']; // 使用 useI18n 获取 t 的类型
// --- 回调函数 ---
onRefresh: () => void;
onUpload: () => void;
onDownload: (items: FileListItem[]) => void; // 文件下载回调
onDownloadDirectory: (item: FileListItem) => void; // +++ 新增:文件夹下载回调 +++
onDownloadDirectory: (item: FileListItem) => void; // +++ 文件夹下载回调 +++
onDelete: () => void; // 删除操作现在由外部处理
onRename: (item: FileListItem) => void;
onChangePermissions: (item: FileListItem) => void;
onNewFolder: () => void;
onNewFile: () => void;
onCopy: () => void; // +++ 新增:复制回调 +++
onCut: () => void; // +++ 新增:剪切回调 +++
onPaste: () => void; // +++ 新增:粘贴回调 +++
onCopy: () => void; // +++ 复制回调 +++
onCut: () => void; // +++ 剪切回调 +++
onPaste: () => void; // +++ 粘贴回调 +++
}
export function useFileManagerContextMenu(options: UseFileManagerContextMenuOptions) {
@@ -111,7 +111,7 @@ export function useFileManagerContextMenu(options: UseFileManagerContextMenuOpti
{ label: t('fileManager.actions.copy'), action: onCopy, disabled: !canPerformActions },
];
// --- 新增:多选下载 ---
// --- 多选下载 ---
// 多选时暂时禁用文件夹下载,只允许下载文件
// 如果需要支持多选文件夹下载或混合下载,需要更复杂的逻辑和后端支持(例如打包成 zip)
// 目前仅在 allFilesSelected 为 true 时启用多文件下载
@@ -31,7 +31,7 @@ const sortFiles = (a: FileListItem, b: FileListItem): number => {
return a.filename.localeCompare(b.filename);
};
// *** 新增:文件树节点接口 ***
// *** 文件树节点接口 ***
export interface FileTreeNode {
filename: string;
longname: string; // 保留 longname 以便显示
@@ -61,16 +61,16 @@ export function createSftpActionsManager(
// const fileList = ref<FileListItem[]>([]); // 不再直接使用 fileList ref
const isLoading = ref<boolean>(false);
const loadingRequestId = ref<string | null>(null); // 新增:跟踪当前加载请求 ID
const loadingRequestId = ref<string | null>(null); // 跟踪当前加载请求 ID
// const error = ref<string | null>(null); // 不再使用本地 error ref
const instanceSessionId = sessionId; // 保存会话 ID 用于日志
const uiNotificationsStore = useUiNotificationsStore(); // 初始化 UI 通知 store
const initialLoadDone = ref<boolean>(false); // +++ 新增:跟踪此实例是否已完成初始加载 +++
const initialLoadDone = ref<boolean>(false); // +++ 跟踪此实例是否已完成初始加载 +++
// 用于存储注销函数的数组
const unregisterCallbacks: (() => void)[] = [];
// *** 新增:响应式文件树 ***
// *** 响应式文件树 ***
const fileTree = reactive<FileTreeNode>({
filename: '/', // 根节点代表根目录
longname: '/',
@@ -223,7 +223,7 @@ export function createSftpActionsManager(
console.warn(`[SFTP ${instanceSessionId}] 尝试加载目录 ${path} 但 SFTP 未就绪。`); // 日志改为中文
return;
}
// *** 新增:如果已经在加载,则阻止新的加载请求 ***
// *** 如果已经在加载,则阻止新的加载请求 ***
if (isLoading.value) {
console.warn(`[SFTP ${instanceSessionId}] 尝试加载目录 ${path} 但已在加载中。`);
return;
@@ -415,7 +415,7 @@ export function createSftpActionsManager(
});
};
// +++ 新增:复制项目 +++
// +++ 复制项目 +++
const copyItems = (sourcePaths: string[], destinationDir: string) => {
if (!isSftpReady.value) {
uiNotificationsStore.showError(t('fileManager.errors.sftpNotReady'));
@@ -433,7 +433,7 @@ export function createSftpActionsManager(
// 可选:显示一个“正在复制...”的通知
};
// +++ 新增:移动项目 +++
// +++ 移动项目 +++
const moveItems = (sourcePaths: string[], destinationDir: string) => {
if (!isSftpReady.value) {
uiNotificationsStore.showError(t('fileManager.errors.sftpNotReady'));
@@ -579,12 +579,12 @@ export function createSftpActionsManager(
// 移除通用的 onActionSuccessRefresh
// *** 新增:具体操作成功后的处理函数 ***
// *** 具体操作成功后的处理函数 ***
// *** 移除旧的 invalidateCache ***
// const invalidateCache = (path: string) => { ... };
// *** 新增:辅助函数 - 从文件树中移除节点 ***
// *** 辅助函数 - 从文件树中移除节点 ***
const removeNodeFromTree = (parentPath: string, filename: string): boolean => {
const parentNode = findNodeByPath(fileTree, parentPath);
if (parentNode && parentNode.children) {
@@ -787,7 +787,7 @@ export function createSftpActionsManager(
}
};
// +++ 新增:处理复制成功 +++
// +++ 处理复制成功 +++
const onCopySuccess = (payload: MessagePayload, message: WebSocketMessage) => {
// 后端应发送 { destination: string, items: FileListItem[] | null }
const copyPayload = payload as { destination: string, items: FileListItem[] | null };
@@ -825,7 +825,7 @@ export function createSftpActionsManager(
}
};
// +++ 新增:处理移动成功 +++
// +++ 处理移动成功 +++
const onMoveSuccess = (payload: MessagePayload, message: WebSocketMessage) => {
// 后端应发送 { sources: string[], destination: string, items: FileListItem[] | null }
const movePayload = payload as { sources: string[], destination: string, items: FileListItem[] | null };
@@ -867,7 +867,7 @@ export function createSftpActionsManager(
};
// *** 新增:处理上传成功 ***
// *** 处理上传成功 ***
const onUploadSuccess = (payload: MessagePayload, message: WebSocketMessage) => {
const newItem = payload as FileListItem | null; // 后端应发送 FileListItem 或 null
const fullPath = message.path; // 后端现在应该在 message 中包含完整的上传路径
@@ -944,14 +944,14 @@ export function createSftpActionsManager(
unregisterCallbacks.push(onMessage('sftp:rename:success', onRenameSuccess));
unregisterCallbacks.push(onMessage('sftp:chmod:success', onChmodSuccess));
unregisterCallbacks.push(onMessage('sftp:writefile:success', onWriteFileSuccess)); // 使用 onWriteFileSuccess
unregisterCallbacks.push(onMessage('sftp:upload:success', onUploadSuccess)); // *** 新增:监听上传成功 ***
unregisterCallbacks.push(onMessage('sftp:upload:success', onUploadSuccess)); // *** 监听上传成功 ***
unregisterCallbacks.push(onMessage('sftp:mkdir:error', onActionError));
unregisterCallbacks.push(onMessage('sftp:rmdir:error', onActionError));
unregisterCallbacks.push(onMessage('sftp:unlink:error', onActionError));
unregisterCallbacks.push(onMessage('sftp:rename:error', onActionError));
unregisterCallbacks.push(onMessage('sftp:chmod:error', onActionError));
unregisterCallbacks.push(onMessage('sftp:writefile:error', onActionError));
// +++ 新增:监听复制/移动错误 +++
// +++ 监听复制/移动错误 +++
unregisterCallbacks.push(onMessage('sftp:copy:success', onCopySuccess));
unregisterCallbacks.push(onMessage('sftp:copy:error', onActionError));
unregisterCallbacks.push(onMessage('sftp:move:success', onMoveSuccess));
@@ -959,7 +959,7 @@ export function createSftpActionsManager(
// 移除 onUnmounted 块
// *** 新增:计算属性 fileList ***
// *** 计算属性 fileList ***
const fileList = computed<FileListItem[]>(() => {
const node = findNodeByPath(fileTree, currentPathRef.value);
if (node && node.childrenLoaded && node.children) {