Add file rename functionality and improve preview handling

- Implement file/folder rename feature with dialog and API endpoint
- Add rename button to file browser with keyboard shortcuts
- Switch PDF preview from object to embed tag for better compatibility
- Fix CORS issues by fetching preview files as blobs with object URLs
- Add proper cleanup for object URLs to prevent memory leaks
- Add renameObject utility function for MinIO operations
This commit is contained in:
2025-06-04 18:14:00 +02:00
parent 673b6c6748
commit bac1bb2b5e
4 changed files with 317 additions and 16 deletions

View File

@@ -118,6 +118,15 @@
<v-icon>mdi-download</v-icon>
<v-tooltip activator="parent" location="top">Download</v-tooltip>
</v-btn>
<v-btn
icon
variant="text"
size="small"
@click.stop="confirmRename(item)"
>
<v-icon>mdi-rename-box</v-icon>
<v-tooltip activator="parent" location="top">Rename</v-tooltip>
</v-btn>
<v-btn
icon
variant="text"
@@ -222,6 +231,41 @@
</v-card>
</v-dialog>
<!-- Rename Dialog -->
<v-dialog v-model="renameDialog" max-width="400">
<v-card>
<v-card-title>
<v-icon class="mr-2">mdi-rename-box</v-icon>
Rename {{ fileToRename?.isFolder ? 'Folder' : 'File' }}
</v-card-title>
<v-card-text>
<v-text-field
v-model="newName"
:label="fileToRename?.isFolder ? 'New folder name' : 'New file name (without extension)'"
variant="outlined"
density="comfortable"
autofocus
@keyup.enter="renameFile"
:hint="fileToRename?.isFolder ? '' : `Extension will be preserved (.${fileToRename?.extension})`"
persistent-hint
/>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn @click="renameDialog = false">Cancel</v-btn>
<v-btn
color="primary"
variant="flat"
@click="renameFile"
:loading="renaming"
:disabled="!newName || newName === getDisplayNameForRename()"
>
Rename
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- File Preview Dialog -->
<FilePreviewModal
v-model="previewDialog"
@@ -255,16 +299,20 @@ const searchQuery = ref('');
const loading = ref(false);
const uploading = ref(false);
const deleting = ref(false);
const renaming = ref(false);
const creatingFolder = ref(false);
const uploadDialog = ref(false);
const deleteDialog = ref(false);
const newFolderDialog = ref(false);
const previewDialog = ref(false);
const renameDialog = ref(false);
const fileToDelete = ref<FileItem | null>(null);
const fileToPreview = ref<FileItem | null>(null);
const fileToRename = ref<FileItem | null>(null);
const downloadingFiles = ref<Record<string, boolean>>({});
const currentPath = ref('');
const newFolderName = ref('');
const newName = ref('');
// Table headers
const headers = [
@@ -461,6 +509,59 @@ const deleteFile = async () => {
}
};
// Confirm rename
const confirmRename = (file: FileItem) => {
fileToRename.value = file;
// Set initial value to current display name without extension
if (file.isFolder) {
newName.value = file.displayName;
} else {
// Remove extension from display name
const extIndex = file.displayName.lastIndexOf('.');
newName.value = extIndex > -1 ? file.displayName.substring(0, extIndex) : file.displayName;
}
renameDialog.value = true;
};
// Get display name for rename comparison
const getDisplayNameForRename = () => {
if (!fileToRename.value) return '';
if (fileToRename.value.isFolder) {
return fileToRename.value.displayName;
} else {
// Remove extension from display name
const extIndex = fileToRename.value.displayName.lastIndexOf('.');
return extIndex > -1 ? fileToRename.value.displayName.substring(0, extIndex) : fileToRename.value.displayName;
}
};
// Rename file
const renameFile = async () => {
if (!fileToRename.value || !newName.value) return;
renaming.value = true;
try {
await $fetch('/api/files/rename', {
method: 'POST',
body: {
oldName: fileToRename.value.name,
newName: newName.value,
isFolder: fileToRename.value.isFolder,
},
});
toast.success(fileToRename.value.isFolder ? 'Folder renamed successfully' : 'File renamed successfully');
renameDialog.value = false;
await loadFiles();
} catch (error: any) {
toast.error(error.data?.statusMessage || 'Failed to rename');
} finally {
renaming.value = false;
fileToRename.value = null;
newName.value = '';
}
};
// Helpers
const formatDate = (date: string) => {
return new Date(date).toLocaleDateString('en-US', {