|
657 | 657 | 'left: 0', |
658 | 658 | 'right: 0', |
659 | 659 | 'bottom: 0', |
660 | | - 'border: 1px dashed rgba(148, 163, 184, 0.9)', |
| 660 | + 'border: 1px dashed rgba(96, 165, 250, 0.9)', |
661 | 661 | 'background: rgba(255, 255, 255, 0.75)', |
662 | 662 | 'pointer-events: none', |
663 | 663 | 'border-radius: 6px', |
|
845 | 845 | } |
846 | 846 |
|
847 | 847 | async function unmountDocument(filePath, isFolder) { |
848 | | - dependencies.showLoadingOverlay(isFolder ? '正在取消挂载文件夹…' : '正在取消挂载…'); |
849 | 848 | try { |
850 | 849 | dependencies.closeAllModals(); |
851 | 850 | const absolutePath = await ensureProjectAbsolutePath(filePath); |
|
906 | 905 | showCancel: false |
907 | 906 | }); |
908 | 907 | } finally { |
909 | | - dependencies.hideLoadingOverlay(); |
910 | 908 | const fileItem = document.querySelector(`[data-path="${filePath}"]`); |
911 | 909 | if (fileItem) { |
912 | 910 | const indicator = fileItem.querySelector('.upload-indicator'); |
|
952 | 950 | if (folderOperationKey) { |
953 | 951 | backendProgressState.folderTasks.delete(folderOperationKey); |
954 | 952 | } |
955 | | - dependencies.hideLoadingOverlay(); |
956 | 953 | } |
957 | 954 | } |
958 | 955 |
|
|
996 | 993 | } |
997 | 994 |
|
998 | 995 | async function unmountFolder(folderPath) { |
999 | | - ensureBackendStatusListener(); |
1000 | | - const overlayMessage = '正在取消挂载文件夹…'; |
1001 | | - dependencies.setLoadingOverlayProgress(0.05, { |
1002 | | - message: overlayMessage, |
1003 | | - stage: '准备中' |
1004 | | - }); |
1005 | 996 | let folderOperationKey = null; |
1006 | 997 | try { |
1007 | 998 | const normalizedPath = await ensureProjectAbsolutePath(folderPath); |
1008 | 999 | const relativeTracking = await getTrackingRelativePath(normalizedPath); |
1009 | | - folderOperationKey = buildFolderOperationKey('unmount_folder', relativeTracking); |
1010 | | - if (folderOperationKey) { |
1011 | | - backendProgressState.folderTasks.set(folderOperationKey, { |
1012 | | - message: overlayMessage, |
1013 | | - total: 0, |
1014 | | - completed: 0 |
1015 | | - }); |
1016 | | - } |
| 1000 | + folderOperationKey = null; |
1017 | 1001 | const response = await fetch('http://localhost:8000/api/document/unmount-folder', { |
1018 | 1002 | method: 'POST', |
1019 | 1003 | headers: { 'Content-Type': 'application/json' }, |
|
1034 | 1018 | } |
1035 | 1019 | } |
1036 | 1020 |
|
1037 | | - function handleFolderOperationResult(title, data) { |
| 1021 | + function handleFolderOperationResult(title, data, options = {}) { |
1038 | 1022 | const failed = data.failed || 0; |
1039 | 1023 | const statusKey = data.status || (failed > 0 ? 'partial' : 'success'); |
1040 | 1024 | if (data.folder && typeof dependencies.refreshFolderUploadIndicators === 'function') { |
|
1053 | 1037 | message = '操作完成,部分项目未成功处理。'; |
1054 | 1038 | } |
1055 | 1039 |
|
| 1040 | + if (statusKey === 'success' && options.suppressSuccessModal) { |
| 1041 | + setTimeout(async () => { |
| 1042 | + const explorer = getExplorerModule(); |
| 1043 | + if (explorer && typeof explorer.refreshFileTree === 'function') { |
| 1044 | + await explorer.refreshFileTree(); |
| 1045 | + } else { |
| 1046 | + await loadFileTree(); |
| 1047 | + } |
| 1048 | + }, 300); |
| 1049 | + return; |
| 1050 | + } |
| 1051 | + |
1056 | 1052 | dependencies.showModal({ |
1057 | 1053 | type: modalType, |
1058 | 1054 | title, |
|
2014 | 2010 | if (isExpanded) { |
2015 | 2011 | state.expandedFolders.delete(node.path); |
2016 | 2012 | childContainer.style.display = 'none'; |
2017 | | - arrow.style.transform = 'rotate(0deg)'; |
| 2013 | + if (arrow) { arrow.style.transform = 'rotate(0deg)'; } |
2018 | 2014 | folderIcon.innerHTML = getFileIcon(node.name, true, false); |
2019 | 2015 | } else { |
2020 | 2016 | state.expandedFolders.add(node.path); |
2021 | 2017 | childContainer.style.display = 'block'; |
2022 | | - arrow.style.transform = 'rotate(90deg)'; |
| 2018 | + if (arrow) { arrow.style.transform = 'rotate(90deg)'; } |
2023 | 2019 | folderIcon.innerHTML = getFileIcon(node.name, true, true); |
2024 | 2020 | if (typeof dependencies.updateFolderUploadStatus === 'function') { |
2025 | 2021 | dependencies.updateFolderUploadStatus(nodeRelativePath); |
|
2092 | 2088 | if (node.children) { |
2093 | 2089 | div.classList.add('folder-item'); |
2094 | 2090 | const isExpanded = state.expandedFolders.has(node.path); |
2095 | | - const arrow = document.createElement('span'); |
2096 | | - arrow.textContent = '▶'; |
2097 | | - arrow.className = 'folder-arrow'; |
2098 | | - arrow.style.transform = isExpanded ? 'rotate(90deg)' : 'rotate(0deg)'; |
2099 | | - contentDiv.appendChild(arrow); |
2100 | 2091 |
|
2101 | 2092 | const nameWrapper = document.createElement('span'); |
2102 | 2093 | nameWrapper.className = 'file-name'; |
| 2094 | + |
| 2095 | + // 还原更小的浅灰色开放式箭头(两段线) |
| 2096 | + const arrow = document.createElement('span'); |
| 2097 | + arrow.className = 'folder-arrow'; |
| 2098 | + arrow.style.color = '#9aa0a6'; |
| 2099 | + arrow.innerHTML = ` |
| 2100 | + <svg width="8" height="8" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> |
| 2101 | + <polyline points="2,1 8,5 2,9" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" /> |
| 2102 | + </svg> |
| 2103 | + `; |
| 2104 | + nameWrapper.appendChild(arrow); |
| 2105 | + |
2103 | 2106 | const folderIcon = document.createElement('span'); |
2104 | 2107 | folderIcon.className = 'file-icon-wrapper'; |
2105 | 2108 | folderIcon.innerHTML = getFileIcon(node.name, true, isExpanded); |
2106 | 2109 | nameWrapper.appendChild(folderIcon); |
| 2110 | + |
2107 | 2111 | const nameSpan = document.createElement('span'); |
2108 | 2112 | nameSpan.className = 'file-name-text'; |
2109 | 2113 | nameSpan.textContent = node.name; |
|
2118 | 2122 | childContainer.dataset.parent = node.path; |
2119 | 2123 | childContainer.dataset.parentRelative = nodeRelativePath; |
2120 | 2124 | childContainer.style.display = isExpanded ? 'block' : 'none'; |
2121 | | - arrow.style.transform = isExpanded ? 'rotate(90deg)' : 'rotate(0deg)'; |
2122 | 2125 | node.children.forEach((child) => renderTree(child, childContainer, false, depth + 1)); |
2123 | 2126 | container.appendChild(childContainer); |
2124 | 2127 |
|
| 2128 | + // 初始化箭头方向 |
| 2129 | + if (arrow) { |
| 2130 | + arrow.style.transform = isExpanded ? 'rotate(90deg)' : 'rotate(0deg)'; |
| 2131 | + } |
| 2132 | + |
2125 | 2133 | if (isExpanded && typeof dependencies.updateFolderUploadStatus === 'function') { |
2126 | 2134 | dependencies.updateFolderUploadStatus(nodeRelativePath); |
2127 | 2135 | } |
|
2146 | 2154 | } else { |
2147 | 2155 | div.classList.add('file-item-file'); |
2148 | 2156 | div.dataset.fileName = node.name; |
| 2157 | + |
2149 | 2158 | const nameWrapper = document.createElement('span'); |
2150 | 2159 | nameWrapper.className = 'file-name'; |
| 2160 | + // 文件圆点,颜色淡灰,与箭头垂直对齐 |
| 2161 | + const fileDot = document.createElement('span'); |
| 2162 | + fileDot.className = 'file-dot'; |
| 2163 | + fileDot.innerHTML = ` |
| 2164 | + <svg width="8" height="8" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> |
| 2165 | + <circle cx="5" cy="5" r="2.5" fill="currentColor" /> |
| 2166 | + </svg> |
| 2167 | + `; |
| 2168 | + nameWrapper.appendChild(fileDot); |
| 2169 | + |
2151 | 2170 | const fileIconWrapper = document.createElement('span'); |
2152 | 2171 | fileIconWrapper.className = 'file-icon-wrapper'; |
2153 | 2172 | fileIconWrapper.innerHTML = getFileIcon(node.name, false); |
|
2204 | 2223 | if (Array.isArray(tree.children)) { |
2205 | 2224 | tree.children.forEach((child) => renderTree(child, state.fileTreeEl, true, 0)); |
2206 | 2225 | } |
| 2226 | + // 空状态占位:当没有任何文件/文件夹时显示提示 |
| 2227 | + if (!Array.isArray(tree.children) || tree.children.length === 0) { |
| 2228 | + const placeholder = document.createElement('div'); |
| 2229 | + placeholder.className = 'file-tree-empty-placeholder'; |
| 2230 | + placeholder.textContent = '你可以新建或导入文件📃'; |
| 2231 | + placeholder.style.cursor = 'pointer'; |
| 2232 | + placeholder.setAttribute('role', 'button'); |
| 2233 | + placeholder.tabIndex = 0; |
| 2234 | + // 顶对齐:确保占位框贴顶部并覆盖可视宽度 |
| 2235 | + // 具体宽度与边距由 CSS 控制,这里只确保不出现额外空隙 |
| 2236 | + placeholder.style.marginTop = '-5px'; |
| 2237 | + // 点击导入:调用 ExplorerModule.importFiles(打开本地文件夹) |
| 2238 | + const explorer = getExplorerModule(); |
| 2239 | + const handleImport = () => { |
| 2240 | + if (explorer && typeof explorer.importFiles === 'function') { |
| 2241 | + explorer.importFiles(); |
| 2242 | + } |
| 2243 | + }; |
| 2244 | + placeholder.addEventListener('click', (e) => { |
| 2245 | + e.preventDefault(); |
| 2246 | + e.stopPropagation(); |
| 2247 | + handleImport(); |
| 2248 | + }); |
| 2249 | + placeholder.addEventListener('keydown', (e) => { |
| 2250 | + if (e.key === 'Enter' || e.key === ' ') { |
| 2251 | + e.preventDefault(); |
| 2252 | + e.stopPropagation(); |
| 2253 | + handleImport(); |
| 2254 | + } |
| 2255 | + }); |
| 2256 | + state.fileTreeEl.appendChild(placeholder); |
| 2257 | + } |
2207 | 2258 | if (typeof dependencies.updateFolderUploadStatus === 'function') { |
2208 | 2259 | await dependencies.updateFolderUploadStatus('data'); |
2209 | 2260 | } |
|
2240 | 2291 | } |
2241 | 2292 | const handleRootDragEnter = (event) => { |
2242 | 2293 | event.preventDefault(); |
2243 | | - if (!isExternalDragEvent(event)) { |
2244 | | - return; |
2245 | | - } |
2246 | 2294 | const targetItem = event.target instanceof Element ? event.target.closest('.file-item[data-path]') : null; |
2247 | 2295 | if (targetItem) { |
2248 | 2296 | hideRootOverlay(); |
2249 | 2297 | return; |
2250 | 2298 | } |
| 2299 | + // 显示根目录虚线边框(支持内部拖拽和外部拖拽) |
2251 | 2300 | showRootOverlay(); |
2252 | 2301 | if (state.dropIndicator) { |
2253 | 2302 | state.dropIndicator.style.display = 'none'; |
|
2260 | 2309 | if (event.dataTransfer) { |
2261 | 2310 | event.dataTransfer.dropEffect = external ? 'copy' : 'move'; |
2262 | 2311 | } |
2263 | | - if (!external) { |
2264 | | - hideRootOverlay(); |
2265 | | - return; |
2266 | | - } |
2267 | 2312 | const targetItem = event.target instanceof Element ? event.target.closest('.file-item[data-path]') : null; |
2268 | 2313 | if (targetItem) { |
2269 | 2314 | hideRootOverlay(); |
2270 | 2315 | return; |
2271 | 2316 | } |
| 2317 | + // 在非目标项区域显示根目录虚线边框(内部/外部拖拽均支持) |
2272 | 2318 | showRootOverlay(); |
2273 | 2319 | if (state.dropIndicator) { |
2274 | 2320 | state.dropIndicator.style.display = 'none'; |
|
0 commit comments