Improve local filetree view state and sidebar performance
This commit is contained in:
@@ -5,6 +5,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
createPage,
|
||||
cssEscape,
|
||||
currentDocumentId,
|
||||
currentRootUri,
|
||||
currentSourceKind,
|
||||
deleteSingleFileTreeAsset,
|
||||
dispatchSidebarEvent,
|
||||
@@ -23,6 +24,8 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
openEditorAttachmentEditTab,
|
||||
openEditorAttachmentNewWindow,
|
||||
refreshLocalFolderSidebarSnapshot,
|
||||
removeFileTreeAssetRow,
|
||||
revealFileTreeResource,
|
||||
resolveWorkspaceId,
|
||||
runtimeState,
|
||||
selectedSidebarFileTreeSelection,
|
||||
@@ -30,6 +33,12 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
validateFileTreeRename,
|
||||
} = dependencies;
|
||||
const sidebarFileTreeSelection = selectedSidebarFileTreeSelection;
|
||||
var fileTreeOperationBatchCounter = 0;
|
||||
|
||||
function nextFileTreeOperationBatchId(action) {
|
||||
fileTreeOperationBatchCounter += 1;
|
||||
return 'filetree-' + String(action || 'operation') + '-' + Date.now() + '-' + fileTreeOperationBatchCounter;
|
||||
}
|
||||
|
||||
function rowTitle(row) {
|
||||
var title = row ? row.querySelector(':scope > .tree-link > .tree-link-title') : null;
|
||||
@@ -59,8 +68,12 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
var rowKind = row.getAttribute('data-row-kind') || '';
|
||||
var documentId = row.getAttribute('data-document-id') || row.getAttribute('data-doc-id') || '';
|
||||
var assetId = row.getAttribute('data-asset-id') || '';
|
||||
if (!documentId && !assetId) return false;
|
||||
if (rowKind && ['document', 'doc', 'index', 'markdown', 'asset'].indexOf(rowKind) < 0 && !assetId) return false;
|
||||
var rowId = row.getAttribute('data-row-id') || '';
|
||||
var localFolderSource = currentSourceKind() === 'local_folder';
|
||||
var localFolderDirectory = localFolderSource && (rowKind === 'folder' || rowKind === 'directory');
|
||||
var commandTargetId = documentId || (localFolderSource ? (assetId || rowId) : '');
|
||||
if (!commandTargetId && !assetId) return false;
|
||||
if (rowKind && ['document', 'doc', 'index', 'markdown', 'asset'].indexOf(rowKind) < 0 && !assetId && !localFolderDirectory) return false;
|
||||
var link = row.querySelector(':scope > .tree-link');
|
||||
var title = rowTitle(row);
|
||||
if (!(link instanceof HTMLElement)) return false;
|
||||
@@ -68,7 +81,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
var input = document.createElement('input');
|
||||
input.type = 'text';
|
||||
input.className = 'tree-rename-input';
|
||||
input.setAttribute('data-rename-id', row.getAttribute('data-row-id') || '');
|
||||
input.setAttribute('data-rename-id', rowId);
|
||||
input.value = title;
|
||||
input.style.minWidth = '0';
|
||||
input.style.flex = '1 1 auto';
|
||||
@@ -117,7 +130,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
var commandTitle = isFileTreePageRow(row) ? normalizeFileTreePageRenameTitle(nextTitle) : nextTitle;
|
||||
committing = true;
|
||||
input.disabled = true;
|
||||
var work = assetId
|
||||
var work = assetId && !localFolderSource
|
||||
? Promise.resolve().then(function(){
|
||||
document.querySelectorAll('#sidebar-file-tree-root .tree-row[data-asset-id="' + cssEscape(assetId) + '"] .tree-link-title').forEach(function(titleNode) {
|
||||
titleNode.textContent = commandTitle;
|
||||
@@ -128,9 +141,11 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
: dispatchTreeCommand(row, {
|
||||
action: 'rename',
|
||||
workspaceId: resolveWorkspaceId(row),
|
||||
documentId: documentId,
|
||||
documentId: commandTargetId,
|
||||
title: commandTitle
|
||||
}).then(function(){ updateTitleEverywhere(documentId, commandTitle); });
|
||||
}).then(function(){
|
||||
if (!localFolderSource && documentId) updateTitleEverywhere(documentId, commandTitle);
|
||||
});
|
||||
void work.then(close).catch(function(error) {
|
||||
committing = false;
|
||||
input.disabled = false;
|
||||
@@ -293,6 +308,51 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
return detail.title || '';
|
||||
}
|
||||
|
||||
function localRootPathFromRootUri(rootUri) {
|
||||
var value = String(rootUri || '').trim();
|
||||
if (!value) return '';
|
||||
if (!/^file:\/\//i.test(value)) return value.charAt(0) === '/' ? value : '';
|
||||
var pathPart = value.replace(/^file:\/\//i, '');
|
||||
if (pathPart.indexOf('localhost/') === 0) pathPart = pathPart.slice('localhost'.length);
|
||||
if (pathPart.charAt(0) !== '/') pathPart = '/' + pathPart;
|
||||
try {
|
||||
return decodeURIComponent(pathPart);
|
||||
} catch (_) {
|
||||
return pathPart;
|
||||
}
|
||||
}
|
||||
|
||||
function joinLocalAbsolutePath(rootUri, relativePath) {
|
||||
var rootPath = localRootPathFromRootUri(rootUri);
|
||||
var normalized = decodeLocalEncodedPath(relativePath).replace(/^\/+/, '');
|
||||
if (!rootPath || !normalized) return rootPath || normalized;
|
||||
return rootPath.replace(/\/+$/g, '') + '/' + normalized;
|
||||
}
|
||||
|
||||
function fileTreeCopyLocalAbsolutePath(detail, trigger) {
|
||||
var rootUri = typeof currentRootUri === 'function' ? currentRootUri() : '';
|
||||
if (!rootUri && trigger && typeof trigger.closest === 'function') {
|
||||
var row = trigger.closest('.tree-row[data-shell-mode="filetree"]');
|
||||
if (row instanceof HTMLElement) rootUri = row.getAttribute('data-root-uri') || '';
|
||||
}
|
||||
if (!rootUri && document.body) rootUri = document.body.getAttribute('data-mnote-root-uri') || '';
|
||||
var relativePath = String(detail && detail.localRelativePath || '').trim();
|
||||
if (!relativePath && detail && detail.assetId) relativePath = localFilePathFromAssetId(detail.assetId);
|
||||
if (!relativePath && detail && detail.documentId) {
|
||||
relativePath = String(detail.documentId || '')
|
||||
.replace(/^local-md:/, '')
|
||||
.replace(/^local-dir:/, '');
|
||||
}
|
||||
if (!relativePath && detail && detail.rowId) {
|
||||
relativePath = String(detail.rowId || '')
|
||||
.replace(/^local:asset:/, '')
|
||||
.replace(/^local:markdown:/, '')
|
||||
.replace(/^local:folder:/, '')
|
||||
.replace(/^local:node:/, '');
|
||||
}
|
||||
return joinLocalAbsolutePath(rootUri, relativePath);
|
||||
}
|
||||
|
||||
function fileTreeMenuTargetParentId(detail, trigger) {
|
||||
var rowKind = String(detail && detail.rowKind || '').trim();
|
||||
var rowId = String(detail && detail.rowId || '').trim();
|
||||
@@ -361,6 +421,10 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
var workspaceId = detail.workspaceId || resolveWorkspaceId(trigger || document.body);
|
||||
var title = detail.title || '无标题';
|
||||
var isAsset = detail.contextKind === 'filetree' && detail.assetId && detail.rowKind !== 'document' && detail.rowKind !== 'index';
|
||||
if (detail.contextKind === 'filetree' && action === 'toggle-sidebar-folder-shortcut') {
|
||||
dispatchSidebarEvent('tree.sidebarShortcut.toggleFolder', detail);
|
||||
return;
|
||||
}
|
||||
if (detail.contextKind === 'filetree' && action === 'download') {
|
||||
downloadSelectedFileTreeAssetRows(detail, trigger);
|
||||
return;
|
||||
@@ -408,7 +472,10 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
return;
|
||||
}
|
||||
if (action === 'copy-id') {
|
||||
void copyTreeContextValue(documentId || detail.assetId || detail.rowId || '', 'copy-id');
|
||||
var copyIdValue = currentSourceKind() === 'local_folder'
|
||||
? fileTreeCopyLocalAbsolutePath(detail, trigger)
|
||||
: '';
|
||||
void copyTreeContextValue(copyIdValue || documentId || detail.assetId || detail.rowId || '', 'copy-id');
|
||||
return;
|
||||
}
|
||||
if (action === 'delete-trash' && detail.contextKind === 'filetree') {
|
||||
@@ -776,6 +843,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
{ separator: true },
|
||||
{ action: 'new-file', icon: 'note_add', label: 'New File', when: '!workspace.readonly' },
|
||||
{ action: 'new-folder', icon: 'create_new_folder', label: 'New Folder', disabled: currentSourceKind() !== 'local_folder', title: currentSourceKind() === 'local_folder' ? '在当前目录下创建子文件夹' : '仅 local folder 支持创建文件夹', when: '!workspace.readonly' },
|
||||
{ action: 'toggle-sidebar-folder-shortcut', icon: 'star', label: '加入/取消星标置顶', disabled: currentSourceKind() !== 'local_folder' || (detail.rowKind !== 'folder' && detail.rowKind !== 'directory'), title: currentSourceKind() === 'local_folder' ? '把当前文件夹加入或移出星标置顶' : '仅 local folder 文件夹支持星标置顶' },
|
||||
{ action: 'paste-into', icon: 'content_paste', label: '粘贴到此处', disabled: !runtimeState.sidebarFileTreeClipboard, title: runtimeState.sidebarFileTreeClipboard ? '粘贴到当前文件树目标' : '剪贴板为空', when: '!workspace.readonly' },
|
||||
{ action: 'refresh', icon: 'refresh', label: 'Refresh' },
|
||||
{ action: 'collapse-all', icon: 'unfold_less', label: 'Collapse All' },
|
||||
@@ -915,22 +983,31 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
var row = document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape('doc:' + id) + '"]')
|
||||
|| document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-document-id="' + cssEscape(id) + '"]')
|
||||
|| document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-doc-id="' + cssEscape(id) + '"]');
|
||||
if (!(row instanceof HTMLElement)) return false;
|
||||
var rowId = row.getAttribute('data-row-id') || '';
|
||||
if (!rowId) return false;
|
||||
selectSidebarFileTreeRow(row, { ctrlKey: false, metaKey: false, shiftKey: false });
|
||||
document.querySelectorAll('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-active="true"]').forEach(function(activeRow) {
|
||||
if (activeRow instanceof HTMLElement) activeRow.setAttribute('data-active', 'false');
|
||||
});
|
||||
row.setAttribute('data-active', 'true');
|
||||
if (options && options.scrollIntoView !== false) {
|
||||
try {
|
||||
row.scrollIntoView({ block: 'nearest' });
|
||||
} catch (_) {
|
||||
row.scrollIntoView();
|
||||
if (!(row instanceof HTMLElement)) {
|
||||
var relativePath = options && options.relativePath
|
||||
? String(options.relativePath || '').trim()
|
||||
: id.indexOf('local-md:') === 0
|
||||
? decodeLocalEncodedPath(id.slice('local-md:'.length))
|
||||
: '';
|
||||
var bundleParentPath = localMarkdownBundleParentPath(relativePath);
|
||||
var bundleRow = bundleParentPath ? visibleFileTreeRowByRelativePath(bundleParentPath) : null;
|
||||
if (bundleRow instanceof HTMLElement) {
|
||||
return activateSidebarFileTreeRow(bundleRow, options);
|
||||
}
|
||||
if (relativePath && typeof revealFileTreeResource === 'function') {
|
||||
void revealFileTreeResource({
|
||||
rootUri: options && options.rootUri,
|
||||
relativePath: relativePath,
|
||||
rowId: options && options.rowId,
|
||||
select: true,
|
||||
focus: true,
|
||||
scroll: options ? options.scrollIntoView !== false : true
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return activateSidebarFileTreeRow(row, options);
|
||||
}
|
||||
|
||||
function selectSidebarFileTreeRowById(rowId, options) {
|
||||
@@ -1063,7 +1140,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
function decodeLocalEncodedPath(value) {
|
||||
var runtimeFn = fileTreeRuntimeFunction('decodeLocalEncodedPath');
|
||||
if (runtimeFn) return runtimeFn(value);
|
||||
var path = String(value || '').trim().replace(/~2F/g, '/');
|
||||
var path = String(value || '').trim().replace(/~([0-9A-Fa-f]{2})/g, '%$1');
|
||||
if (!path) return '';
|
||||
try {
|
||||
return decodeURIComponent(path);
|
||||
@@ -1072,6 +1149,62 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeFileTreeRelativePath(value) {
|
||||
var normalized = String(value || '').trim().replace(/^\/+|\/+$/g, '');
|
||||
return normalized === '.' ? '' : normalized;
|
||||
}
|
||||
|
||||
function parentRelativePathForFileTreePath(relativePath) {
|
||||
var normalized = normalizeFileTreeRelativePath(relativePath);
|
||||
if (!normalized) return '';
|
||||
var index = normalized.lastIndexOf('/');
|
||||
return index > 0 ? normalized.slice(0, index) : '';
|
||||
}
|
||||
|
||||
function fileNameStem(value) {
|
||||
var name = String(value || '').trim();
|
||||
var dot = name.lastIndexOf('.');
|
||||
return dot > 0 ? name.slice(0, dot) : name;
|
||||
}
|
||||
|
||||
function localMarkdownBundleParentPath(relativePath) {
|
||||
var normalized = normalizeFileTreeRelativePath(relativePath);
|
||||
if (!/\.md$/i.test(normalized)) return '';
|
||||
var parent = parentRelativePathForFileTreePath(normalized);
|
||||
if (!parent) return '';
|
||||
var fileName = normalized.slice(normalized.lastIndexOf('/') + 1);
|
||||
var parentName = parent.slice(parent.lastIndexOf('/') + 1);
|
||||
return fileNameStem(fileName) === parentName ? parent : '';
|
||||
}
|
||||
|
||||
function visibleFileTreeRowByRelativePath(relativePath) {
|
||||
var normalized = normalizeFileTreeRelativePath(relativePath);
|
||||
if (!normalized) return null;
|
||||
var row = document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-local-relative-path="' + cssEscape(normalized) + '"]');
|
||||
if (!(row instanceof HTMLElement)) return null;
|
||||
if (row.closest('.tree-children--collapsed')) return null;
|
||||
return row;
|
||||
}
|
||||
|
||||
function activateSidebarFileTreeRow(row, options) {
|
||||
if (!(row instanceof HTMLElement)) return false;
|
||||
var rowId = row.getAttribute('data-row-id') || '';
|
||||
if (!rowId) return false;
|
||||
selectSidebarFileTreeRow(row, { ctrlKey: false, metaKey: false, shiftKey: false });
|
||||
document.querySelectorAll('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-active="true"]').forEach(function(activeRow) {
|
||||
if (activeRow instanceof HTMLElement) activeRow.setAttribute('data-active', 'false');
|
||||
});
|
||||
row.setAttribute('data-active', 'true');
|
||||
if (options && options.scrollIntoView !== false) {
|
||||
try {
|
||||
row.scrollIntoView({ block: 'nearest' });
|
||||
} catch (_) {
|
||||
row.scrollIntoView();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function fileTreeRowLocalRelativePath(row) {
|
||||
var runtimeFn = fileTreeRuntimeFunction('fileTreeRowLocalRelativePath');
|
||||
if (runtimeFn) return runtimeFn(row, fileTreeRuntimeDeps());
|
||||
@@ -1239,7 +1372,8 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
var assetRows = [];
|
||||
rows.forEach(function(row) {
|
||||
var kind = fileTreeRowKind(row);
|
||||
if ((kind === 'document' || kind === 'doc' || kind === 'index' || kind === 'markdown') && fileTreeRowDocumentId(row)) {
|
||||
var documentId = fileTreeRowDocumentId(row);
|
||||
if ((kind === 'document' || kind === 'doc' || kind === 'index' || kind === 'markdown' || documentId.indexOf('local-md:') === 0) && documentId) {
|
||||
docRows.push(row);
|
||||
return;
|
||||
}
|
||||
@@ -1338,69 +1472,101 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
return children.length;
|
||||
}
|
||||
|
||||
function fileTreeMoveTargetParentId(targetRow) {
|
||||
if (!(targetRow instanceof HTMLElement)) return null;
|
||||
return fileTreeMenuTargetParentId({
|
||||
rowKind: fileTreeRowKind(targetRow),
|
||||
rowId: targetRow.getAttribute('data-row-id') || '',
|
||||
documentId: fileTreeRowDocumentId(targetRow)
|
||||
}, targetRow) || null;
|
||||
}
|
||||
|
||||
function fileTreeMoveSourceId(row) {
|
||||
if (!(row instanceof HTMLElement)) return '';
|
||||
return fileTreeRowDocumentId(row)
|
||||
|| (currentSourceKind() === 'local_folder'
|
||||
? (fileTreeRowAssetId(row) || row.getAttribute('data-row-id') || '')
|
||||
: fileTreeRowAssetId(row));
|
||||
}
|
||||
|
||||
async function moveSidebarFileTreeRows(rowIds, targetRow, options) {
|
||||
var rows = fileTreeRowsByRowIds(rowIds || []);
|
||||
if (rows.length === 0) return false;
|
||||
var copy = Boolean(options && options.copy);
|
||||
var targetParentId = fileTreeMoveTargetParentId(targetRow);
|
||||
var workspaceId = resolveWorkspaceId(targetRow || document.body);
|
||||
var writable = await ensureFileTreeWritableTarget('move', targetRow, rowIds || [], copy);
|
||||
if (!writable) return false;
|
||||
var batchId = nextFileTreeOperationBatchId(copy ? 'copy' : 'move');
|
||||
var plan = buildSidebarFileTreeDeletePlan(rows);
|
||||
var failures = [];
|
||||
if (copy) {
|
||||
dispatchSidebarEvent('tree.filetree.copy-requested', { rowIds: rowIds || [], targetDocumentId: targetParentId });
|
||||
recordFileTreeActionStatus('copy-requested', { documentId: targetParentId, batchId: batchId });
|
||||
return true;
|
||||
}
|
||||
var movableRows = plan.docRows.concat(plan.folderRows);
|
||||
if (currentSourceKind() === 'local_folder') {
|
||||
movableRows = movableRows.concat(plan.fileAssetRows, plan.mindmapRows, plan.tableRows);
|
||||
}
|
||||
for (var i = 0; i < movableRows.length; i += 1) {
|
||||
var sourceRow = movableRows[i];
|
||||
var sourceId = fileTreeMoveSourceId(sourceRow);
|
||||
if (!sourceId || sourceId === targetParentId) continue;
|
||||
try {
|
||||
await dispatchTreeCommand(targetRow || sourceRow, {
|
||||
action: 'move',
|
||||
batchId: batchId,
|
||||
workspaceId: workspaceId,
|
||||
documentId: sourceId,
|
||||
parentId: targetParentId,
|
||||
sortOrder: targetParentId ? fileTreeChildCount(targetParentId) + i : i
|
||||
});
|
||||
} catch (error) {
|
||||
failures.push(sourceId + ': ' + (error && error.message ? error.message : '移动失败'));
|
||||
}
|
||||
}
|
||||
if (currentSourceKind() !== 'local_folder') {
|
||||
var assetIds = plan.fileAssetRows.map(fileTreeRowAssetId).filter(Boolean);
|
||||
if (assetIds.length > 0) {
|
||||
try {
|
||||
await postSidebarFileTreeJson('/api/media/batch', {
|
||||
action: 'move',
|
||||
assetIds: assetIds,
|
||||
targetDocumentId: targetParentId
|
||||
});
|
||||
} catch (error) {
|
||||
failures.push(assetIds.join(',') + ': ' + (error && error.message ? error.message : '移动附件失败'));
|
||||
}
|
||||
}
|
||||
}
|
||||
window.dispatchEvent(new CustomEvent('tree:local-command-batch-complete', { detail: { batchId: batchId, action: 'move', failed: failures.length, count: movableRows.length } }));
|
||||
if (failures.length > 0) {
|
||||
recordFileTreeActionStatus('failed', { documentId: targetParentId, fallback: 'alert', batchId: batchId });
|
||||
window.alert('部分对象移动失败:' + failures.join(';'));
|
||||
return false;
|
||||
}
|
||||
recordFileTreeActionStatus('applied', { documentId: targetParentId, batchId: batchId });
|
||||
return true;
|
||||
}
|
||||
|
||||
async function pasteSidebarFileTreeClipboard(trigger) {
|
||||
if (!runtimeState.sidebarFileTreeClipboard || !Array.isArray(runtimeState.sidebarFileTreeClipboard.rowIds) || runtimeState.sidebarFileTreeClipboard.rowIds.length === 0) return false;
|
||||
var targetRow = trigger instanceof HTMLElement ? trigger : null;
|
||||
if (!targetRow && sidebarFileTreeSelection.focusedRowId) {
|
||||
targetRow = document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape(sidebarFileTreeSelection.focusedRowId) + '"]');
|
||||
}
|
||||
var targetDocumentId = fileTreeRowDocumentId(targetRow) || currentDocumentId();
|
||||
if (!targetDocumentId) return false;
|
||||
var targetDocumentId = fileTreeMoveTargetParentId(targetRow) || currentDocumentId();
|
||||
var action = runtimeState.sidebarFileTreeClipboard.action === 'cut' ? 'move' : 'copy';
|
||||
var rows = fileTreeRowsByRowIds(runtimeState.sidebarFileTreeClipboard.rowIds);
|
||||
if (rows.length === 0) return false;
|
||||
var writable = await ensureFileTreeWritableTarget('paste', targetRow, runtimeState.sidebarFileTreeClipboard.rowIds, action === 'copy');
|
||||
if (!writable) return false;
|
||||
recordFileTreeAction('paste', {
|
||||
rowId: targetRow ? targetRow.getAttribute('data-row-id') || '' : '',
|
||||
documentId: targetDocumentId,
|
||||
sourceRowIds: runtimeState.sidebarFileTreeClipboard.rowIds,
|
||||
clipboardAction: runtimeState.sidebarFileTreeClipboard.action
|
||||
});
|
||||
var plan = buildSidebarFileTreeDeletePlan(rows);
|
||||
var workspaceId = resolveWorkspaceId(targetRow || document.body);
|
||||
var failures = [];
|
||||
if (action === 'copy') {
|
||||
dispatchSidebarEvent('tree.filetree.copy-requested', { rowIds: runtimeState.sidebarFileTreeClipboard.rowIds, targetDocumentId: targetDocumentId });
|
||||
recordFileTreeActionStatus('copy-requested', { documentId: targetDocumentId });
|
||||
return true;
|
||||
}
|
||||
for (var i = 0; i < plan.docRows.length; i += 1) {
|
||||
var docRow = plan.docRows[i];
|
||||
var documentId = fileTreeRowDocumentId(docRow);
|
||||
if (!documentId || documentId === targetDocumentId) continue;
|
||||
try {
|
||||
await dispatchTreeCommand(targetRow || docRow, {
|
||||
action: 'move',
|
||||
workspaceId: workspaceId,
|
||||
documentId: documentId,
|
||||
parentId: targetDocumentId,
|
||||
sortOrder: fileTreeChildCount(targetDocumentId) + i
|
||||
});
|
||||
} catch (error) {
|
||||
failures.push(documentId + ': ' + (error && error.message ? error.message : '移动页面失败'));
|
||||
}
|
||||
}
|
||||
var assetIds = plan.fileAssetRows.map(fileTreeRowAssetId).filter(Boolean);
|
||||
if (assetIds.length > 0) {
|
||||
try {
|
||||
await postSidebarFileTreeJson('/api/media/batch', {
|
||||
action: 'move',
|
||||
assetIds: assetIds,
|
||||
targetDocumentId: targetDocumentId
|
||||
});
|
||||
} catch (error) {
|
||||
failures.push(assetIds.join(',') + ': ' + (error && error.message ? error.message : '移动附件失败'));
|
||||
}
|
||||
}
|
||||
if (failures.length > 0) {
|
||||
recordFileTreeActionStatus('failed', { documentId: targetDocumentId, fallback: 'alert' });
|
||||
window.alert('部分对象移动失败:' + failures.join(';'));
|
||||
return false;
|
||||
}
|
||||
runtimeState.sidebarFileTreeClipboard = null;
|
||||
recordFileTreeActionStatus('applied', { documentId: targetDocumentId });
|
||||
return true;
|
||||
var ok = await moveSidebarFileTreeRows(runtimeState.sidebarFileTreeClipboard.rowIds, targetRow, { copy: action === 'copy' });
|
||||
if (ok && action === 'move') runtimeState.sidebarFileTreeClipboard = null;
|
||||
return ok;
|
||||
}
|
||||
|
||||
async function deleteSelectedSidebarFileTreeRows(trigger) {
|
||||
@@ -1409,8 +1575,10 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
var total = plan.docRows.length + plan.folderRows.length + plan.fileAssetRows.length + plan.mindmapRows.length + plan.tableRows.length;
|
||||
if (total === 0) return false;
|
||||
if (!window.confirm(sidebarFileTreeDeleteConfirmText(plan))) return false;
|
||||
recordFileTreeAction('bulk-delete', { rowId: trigger instanceof HTMLElement ? trigger.getAttribute('data-row-id') || '' : '', count: total });
|
||||
recordFileTreeActionStatus('pending', { count: total });
|
||||
var batchId = nextFileTreeOperationBatchId('bulk-delete');
|
||||
document.documentElement.setAttribute('data-mnote-filetree-bulk-delete-batch-id', batchId);
|
||||
recordFileTreeAction('bulk-delete', { rowId: trigger instanceof HTMLElement ? trigger.getAttribute('data-row-id') || '' : '', count: total, batchId: batchId });
|
||||
recordFileTreeActionStatus('pending', { count: total, batchId: batchId });
|
||||
var failures = [];
|
||||
for (var i = 0; i < plan.docRows.length; i += 1) {
|
||||
var docRow = plan.docRows[i];
|
||||
@@ -1418,6 +1586,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
try {
|
||||
await dispatchTreeCommand(trigger || docRow, {
|
||||
action: 'archive',
|
||||
batchId: batchId,
|
||||
workspaceId: resolveWorkspaceId(docRow),
|
||||
documentId: documentId
|
||||
});
|
||||
@@ -1432,6 +1601,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
try {
|
||||
await dispatchTreeCommand(trigger || folderRow, {
|
||||
action: 'archive',
|
||||
batchId: batchId,
|
||||
workspaceId: resolveWorkspaceId(folderRow),
|
||||
documentId: folderId
|
||||
});
|
||||
@@ -1446,6 +1616,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
for (var lf = 0; lf < fileAssetIds.length; lf += 1) {
|
||||
await dispatchTreeCommand(trigger || plan.fileAssetRows[lf], {
|
||||
action: 'archive',
|
||||
batchId: batchId,
|
||||
workspaceId: resolveWorkspaceId(plan.fileAssetRows[lf]),
|
||||
documentId: fileAssetIds[lf]
|
||||
});
|
||||
@@ -1455,7 +1626,9 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
}
|
||||
fileAssetIds.forEach(removeFileTreeAssetRow);
|
||||
} catch (error) {
|
||||
failures = failures.concat(fileAssetIds);
|
||||
failures = failures.concat(fileAssetIds.map(function(assetId) {
|
||||
return assetId + ': ' + (error && error.message ? error.message : '删除附件失败');
|
||||
}));
|
||||
}
|
||||
}
|
||||
for (var m = 0; m < plan.mindmapRows.length; m += 1) {
|
||||
@@ -1465,6 +1638,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
if (currentSourceKind() === 'local_folder') {
|
||||
await dispatchTreeCommand(trigger || mindmapRow, {
|
||||
action: 'archive',
|
||||
batchId: batchId,
|
||||
workspaceId: resolveWorkspaceId(mindmapRow),
|
||||
documentId: mindmapId
|
||||
});
|
||||
@@ -1485,6 +1659,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
if (currentSourceKind() === 'local_folder') {
|
||||
await dispatchTreeCommand(trigger || tableRow, {
|
||||
action: 'archive',
|
||||
batchId: batchId,
|
||||
workspaceId: resolveWorkspaceId(tableRow),
|
||||
documentId: tableId
|
||||
});
|
||||
@@ -1502,13 +1677,15 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
sidebarFileTreeSelection.focusedRowId = null;
|
||||
syncSidebarFileTreeSelection();
|
||||
if (failures.length > 0) {
|
||||
recordFileTreeActionStatus('failed', { count: total, failures: failures.slice(0, 20), fallback: 'alert' });
|
||||
window.dispatchEvent(new CustomEvent('tree:local-command-batch-complete', { detail: { batchId: batchId, action: 'bulk-delete', failed: failures.length, count: total } }));
|
||||
recordFileTreeActionStatus('failed', { count: total, failures: failures.slice(0, 20), fallback: 'alert', batchId: batchId });
|
||||
window.alert('部分对象删除失败:' + failures.slice(0, 5).join(', ') + (failures.length > 5 ? '…' : ''));
|
||||
return false;
|
||||
}
|
||||
document.documentElement.setAttribute('data-mnote-filetree-bulk-delete-applied', 'true');
|
||||
recordFileTreeActionStatus('archived', { count: total, undo: 'trash-modal' });
|
||||
if (currentSourceKind() === 'local_folder') void refreshLocalFolderSidebarSnapshot();
|
||||
window.dispatchEvent(new CustomEvent('tree:local-command-batch-complete', { detail: { batchId: batchId, action: 'bulk-delete', failed: 0, count: total } }));
|
||||
recordFileTreeActionStatus('archived', { count: total, undo: 'trash-modal', batchId: batchId });
|
||||
if (currentSourceKind() !== 'local_folder') void refreshLocalFolderSidebarSnapshot();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1565,6 +1742,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
postSidebarFileTreeJson,
|
||||
fileTreeRowsByRowIds,
|
||||
fileTreeChildCount,
|
||||
moveSidebarFileTreeRows,
|
||||
pasteSidebarFileTreeClipboard,
|
||||
deleteSelectedSidebarFileTreeRows,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user