From 89fe4fc560dda8c90c7b6459d809dfc0f1a8e380 Mon Sep 17 00:00:00 2001 From: Achuan-2 Date: Mon, 29 Sep 2025 12:03:16 +0800 Subject: [PATCH] Doc tree supports `Shift+Click` to select multiple documents (#15965) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ feat(文件管理): 支持 Shift+点击多选文档 - 添加 lastClickedFileItem 属性以跟踪最后点击的文档项 - 实现 Shift+点击选择范围功能 - 更新文档项选择状态 * 🎨 文档树「在页签下侧打开」快捷键改为ctrl+shift+click * 🎨 文档树「在页签下侧打开」快捷键改为ctrl+shift+click --- app/src/layout/dock/Files.ts | 41 +++++++++++++++++++++++++++++++++++- app/src/menus/util.ts | 2 +- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/app/src/layout/dock/Files.ts b/app/src/layout/dock/Files.ts index acefac5a1..9d15f728b 100644 --- a/app/src/layout/dock/Files.ts +++ b/app/src/layout/dock/Files.ts @@ -34,6 +34,7 @@ export class Files extends Model { public parent: Tab; private actionsElement: HTMLElement; public closeElement: HTMLElement; + private lastClickedFileItem: HTMLElement = null; constructor(options: { tab: Tab, app: App }) { super({ @@ -333,9 +334,47 @@ export class Files extends Model { } else if (target.tagName === "LI") { if (isOnlyMeta(event) && !event.altKey && !event.shiftKey) { target.classList.toggle("b3-list-item--focus"); + } else if (event.shiftKey && !event.altKey && !isOnlyMeta(event) && target.getAttribute("data-type") === "navigation-file") { + // Shift+click 多选文档 + event.preventDefault(); + event.stopPropagation(); + + if (!this.lastClickedFileItem) { + this.lastClickedFileItem = target; + this.setCurrent(target, false); + return; + } + + // 获取所有文档项 + const allFiles = Array.from(this.element.querySelectorAll('li[data-type="navigation-file"]')); + + // 获取起始和结束索引 + const startIndex = allFiles.indexOf(this.lastClickedFileItem); + const endIndex = allFiles.indexOf(target); + + if (startIndex === -1 || endIndex === -1) return; + + // 确定选择范围 + const start = Math.min(startIndex, endIndex); + const end = Math.max(startIndex, endIndex); + + // 清除现有选择 + allFiles.forEach(file => { + (file as HTMLElement).classList.remove("b3-list-item--focus"); + }); + + // 添加新选择 + for (let i = start; i <= end; i++) { + (allFiles[i] as HTMLElement).classList.add("b3-list-item--focus"); + } + + needFocus = false; + return; } else { this.setCurrent(target, false); if (target.getAttribute("data-type") === "navigation-file") { + // 更新最后点击的文档项 + this.lastClickedFileItem = target; needFocus = false; if (target.getAttribute("data-opening")) { return; @@ -351,7 +390,7 @@ export class Files extends Model { target.removeAttribute("data-opening"); } }); - } else if (!event.altKey && isNotCtrl(event) && event.shiftKey) { + } else if (!event.altKey && !isNotCtrl(event) && event.shiftKey) { openFileById({ app: options.app, id: target.getAttribute("data-node-id"), diff --git a/app/src/menus/util.ts b/app/src/menus/util.ts index 229f64bed..993b2331c 100644 --- a/app/src/menus/util.ts +++ b/app/src/menus/util.ts @@ -67,7 +67,7 @@ export const openEditorTab = (app: App, ids: string[], notebookId?: string, path id: "insertBottom", icon: "iconLayoutBottom", label: window.siyuan.languages.insertBottom, - accelerator: ids.length === 1 ? "⇧" + window.siyuan.languages.click : "", + accelerator: ids.length === 1 ? "⇧⌘" + window.siyuan.languages.click : "", click: () => { if (notebookId) { openFileById({