// 多选框选功能模块(复制自 client/js/disk/multiple-selection.js) class ResourceManagerMultipleSelection { constructor(options) { // 必需的元素 this.container = options.container; // 框选容器(dropZone) this.itemsContainer = options.itemsContainer; // 文件项容器(fileList) this.selectionBox = options.selectionBox; // 选框元素 this.selectionBar = options.selectionBar; // 选择操作栏 this.selectionCount = options.selectionCount; // 选择计数元素 // 选择项的选择器 this.itemSelector = options.itemSelector || '.file-item'; // 回调函数 this.onSelectionChange = options.onSelectionChange || null; // 状态 this.selectedItems = new Set(); this.isSelecting = false; this.selectionStart = null; this.selectionHasMoved = false; this.selectionDragJustFinished = false; this.init(); } init() { this.bindEvents(); } bindEvents() { // 框选事件 this.container.addEventListener('mousedown', (e) => this.startSelection(e)); document.addEventListener('mousemove', (e) => this.updateSelection(e)); document.addEventListener('mouseup', () => this.endSelection()); // 窗口失去焦点时结束框选 window.addEventListener('blur', () => this.endSelection()); // 拖拽结束时也结束框选 document.addEventListener('dragend', () => this.endSelection()); // 点击空白处取消选择 this.container.addEventListener('click', (e) => this.handleContainerClick(e)); } // 框选开始 startSelection(e) { // 只响应左键 if (e.button !== 0) return; // 如果点击的是文件项或其子元素,不启动框选 if (e.target.closest(this.itemSelector)) { return; } // 如果点击的是重命名输入框,不启动框选 if (e.target.classList.contains('rename-input')) { return; } // 如果有输入框正在获得焦点(重命名中),不阻止默认行为,让 blur 触发 const activeInput = document.querySelector('.rename-input:focus'); if (activeInput) { return; } // 阻止默认行为,防止浏览器的文本选择和拖拽干扰框选 e.preventDefault(); // 如果没有按 Ctrl,先清除之前的选择(Windows 行为) if (!e.ctrlKey) { this.clearSelection(); } this.isSelecting = true; this.selectionHasMoved = false; this.selectionStart = { x: e.clientX, y: e.clientY }; this.selectionBox.style.display = 'block'; this.selectionBox.style.left = e.clientX + 'px'; this.selectionBox.style.top = e.clientY + 'px'; this.selectionBox.style.width = '0'; this.selectionBox.style.height = '0'; // 防止拖拽时选中文本 document.body.style.userSelect = 'none'; } // 框选更新 updateSelection(e) { if (!this.isSelecting || !this.selectionStart) return; // 阻止默认行为 e.preventDefault(); const x = Math.min(e.clientX, this.selectionStart.x); const y = Math.min(e.clientY, this.selectionStart.y); const width = Math.abs(e.clientX - this.selectionStart.x); const height = Math.abs(e.clientY - this.selectionStart.y); // 检测是否有实际移动(超过5像素算作拖拽) if (width > 5 || height > 5) { this.selectionHasMoved = true; } this.selectionBox.style.left = x + 'px'; this.selectionBox.style.top = y + 'px'; this.selectionBox.style.width = width + 'px'; this.selectionBox.style.height = height + 'px'; // 检测框选区域内的文件 const boxRect = { left: x, top: y, right: x + width, bottom: y + height }; const items = this.itemsContainer.querySelectorAll(this.itemSelector); items.forEach(item => { const itemRect = item.getBoundingClientRect(); const isIntersecting = !( itemRect.right < boxRect.left || itemRect.left > boxRect.right || itemRect.bottom < boxRect.top || itemRect.top > boxRect.bottom ); if (isIntersecting) { this.selectItem(item, false); } else if (!e.ctrlKey) { this.deselectItem(item, false); } }); this.updateSelectionBar(); this.triggerSelectionChange(); } // 框选结束 endSelection() { // 只有实际拖拽移动过才标记,防止 click 事件清除选择 if (this.isSelecting && this.selectionHasMoved) { this.selectionDragJustFinished = true; } this.isSelecting = false; this.selectionStart = null; this.selectionHasMoved = false; this.selectionBox.style.display = 'none'; // 恢复文本选择 document.body.style.userSelect = ''; } // 处理容器点击 handleContainerClick(e) { // 如果刚完成拖拽框选,不清除选择 if (this.selectionDragJustFinished) { this.selectionDragJustFinished = false; return; } // 如果点击的是重命名输入框,不处理 if (e.target.classList.contains('rename-input')) { return; } // 如果点击的不是文件项,清除选择 if (!e.target.closest(this.itemSelector)) { this.clearSelection(); } } // 选中项目 selectItem(item, updateBar = true) { const path = item.dataset.path; if (!this.selectedItems.has(path)) { this.selectedItems.add(path); item.classList.add('selected'); if (updateBar) { this.updateSelectionBar(); this.triggerSelectionChange(); } } } // 取消选中项目 deselectItem(item, updateBar = true) { const path = item.dataset.path; if (this.selectedItems.has(path)) { this.selectedItems.delete(path); item.classList.remove('selected'); if (updateBar) { this.updateSelectionBar(); this.triggerSelectionChange(); } } } // 切换选中状态(Ctrl+点击) toggleSelection(item) { const path = item.dataset.path; if (this.selectedItems.has(path)) { this.deselectItem(item); } else { this.selectItem(item); } } // 只选中一个项目,取消其他选择(Windows 单击行为) selectOnly(item) { // 先清除所有选择(但不触发回调) this.itemsContainer.querySelectorAll(`${this.itemSelector}.selected`).forEach(el => { el.classList.remove('selected'); }); this.selectedItems.clear(); // 选中当前项 const path = item.dataset.path; this.selectedItems.add(path); item.classList.add('selected'); this.updateSelectionBar(); this.triggerSelectionChange(); } // 清除所有选择 clearSelection() { this.selectedItems.clear(); this.itemsContainer.querySelectorAll(`${this.itemSelector}.selected`).forEach(item => { item.classList.remove('selected'); }); this.updateSelectionBar(); this.triggerSelectionChange(); } // 更新选择操作栏 updateSelectionBar() { const count = this.selectedItems.size; if (this.selectionBar) { if (count > 0) { this.selectionBar.classList.add('show'); if (this.selectionCount) { this.selectionCount.textContent = `已选择 ${count} 项`; } } else { this.selectionBar.classList.remove('show'); } } } // 触发选择变化回调 triggerSelectionChange() { if (this.onSelectionChange) { this.onSelectionChange(this.getSelectedItems()); } } // 获取已选择的项目路径 getSelectedItems() { return Array.from(this.selectedItems); } // 获取已选择的项目数量 getSelectedCount() { return this.selectedItems.size; } // 检查是否有选中项 hasSelection() { return this.selectedItems.size > 0; } // 检查指定项是否被选中 isSelected(path) { return this.selectedItems.has(path); } // 全选所有项目 selectAll() { const items = this.itemsContainer.querySelectorAll(this.itemSelector); items.forEach(item => { const path = item.dataset.path; this.selectedItems.add(path); item.classList.add('selected'); }); this.updateSelectionBar(); this.triggerSelectionChange(); } }