From 1aa5f6853f6d0fbc81597d62fc9ba533b70196ad Mon Sep 17 00:00:00 2001 From: yanghao05 Date: Tue, 8 Apr 2025 21:37:20 +0800 Subject: [PATCH] feat: update media upload page --- frontend/admin/src/pages/MediaPage.vue | 169 +---------- frontend/admin/src/pages/MediaUploadPage.vue | 277 +++++++++++++++++++ frontend/admin/src/router.js | 5 + 3 files changed, 286 insertions(+), 165 deletions(-) create mode 100644 frontend/admin/src/pages/MediaUploadPage.vue diff --git a/frontend/admin/src/pages/MediaPage.vue b/frontend/admin/src/pages/MediaPage.vue index 22e97c3..3534b8a 100644 --- a/frontend/admin/src/pages/MediaPage.vue +++ b/frontend/admin/src/pages/MediaPage.vue @@ -6,33 +6,23 @@ import Button from 'primevue/button'; import Column from 'primevue/column'; import ConfirmDialog from 'primevue/confirmdialog'; import DataTable from 'primevue/datatable'; -import Dialog from 'primevue/dialog'; import Dropdown from 'primevue/dropdown'; -import FileUpload from 'primevue/fileupload'; import ProgressSpinner from 'primevue/progressspinner'; import Toast from 'primevue/toast'; import { useConfirm } from 'primevue/useconfirm'; import { useToast } from 'primevue/usetoast'; import { computed, onMounted, ref } from 'vue'; +import { useRouter } from 'vue-router'; // Import useConfirm dynamically to avoid the error when the service is not provided const confirm = useConfirm(); const toast = useToast(); -// Add ref for upload dialog visibility -const uploadDialogVisible = ref(false); +const router = useRouter(); -// Add ref for FileUpload component -const fileUploadRef = ref(null); - -// Function to open upload dialog +// Remove upload related refs and methods const openUploadDialog = () => { - uploadDialogVisible.value = true; -}; - -// Function to close upload dialog -const closeUploadDialog = () => { - uploadDialogVisible.value = false; + router.push('/medias/uploads'); }; // Media types for filtering @@ -66,124 +56,6 @@ const totalPages = computed(() => { // Sample data - in a real app, this would come from an API const mediaFiles = ref([]); -// Add OSS upload token state -const ossConfig = ref(null); - -// Add method to get OSS token -const getOssToken = async () => { - try { - const response = await mediaService.getUploadToken(); - ossConfig.value = response; - } catch (error) { - toast.add({ severity: 'error', summary: '错误', detail: '获取上传凭证失败', life: 3000 }); - } -}; - -const uploadProgress = ref(0); -const isUploading = ref(false); -const currentFile = ref(null); - -// Add custom uploader method -const customUploader = async (event) => { - const { files } = event; - - try { - for (const file of files) { - currentFile.value = file; - isUploading.value = true; - uploadProgress.value = 0; - - // Get fresh token for each file - await getOssToken(); - - const formData = new FormData(); - formData.append('success_action_status', '200'); - formData.append('policy', ossConfig.value.policy); - formData.append('x-oss-signature', ossConfig.value.signature); - formData.append('x-oss-signature-version', 'OSS4-HMAC-SHA256'); - formData.append('x-oss-credential', ossConfig.value.x_oss_credential); - formData.append('x-oss-date', ossConfig.value.x_oss_date); - formData.append('key', ossConfig.value.dir + file.name); - formData.append('x-oss-security-token', ossConfig.value.security_token); - formData.append('callback', ossConfig.value.callback); - formData.append('file', file); - - const xhr = new XMLHttpRequest(); - xhr.upload.addEventListener('progress', (e) => { - if (e.lengthComputable) { - uploadProgress.value = (e.loaded * 100) / e.total; - } - }); - - await new Promise((resolve, reject) => { - xhr.onreadystatechange = () => { - if (xhr.readyState === 4) { - if (xhr.status >= 200 && xhr.status < 300) { - resolve(xhr.response); - } else { - reject(new Error(`Upload failed with status: ${xhr.status}`)); - } - } - }; - - xhr.open('POST', ossConfig.value.host, true); - xhr.send(formData); - }); - - // Create media record after successful upload - // await mediaService.createMedia({ - // name: file.name, - // file_size: file.size, - // file_type: file.type, - // url: `${ossConfig.value.host}/${ossConfig.value.dir}${file.name}` - // }); - } - - toast.add({ severity: 'success', summary: '成功', detail: '文件上传成功', life: 3000 }); - closeUploadDialog(); - fetchMediaFiles(); - } catch (error) { - toast.add({ severity: 'error', summary: '错误', detail: '文件上传失败', life: 3000 }); - // Use ref to clear the upload queue - fileUploadRef.value.clear(); - } finally { - isUploading.value = false; - currentFile.value = null; - } -}; - -// Add beforeUpload handler -const onBeforeUpload = async (event) => { - if (!ossConfig.value) { - await getOssToken(); - } -}; - -// Preview file -const previewFile = (file) => { - toast.add({ severity: 'info', summary: '预览', detail: `预览 ${file.name}`, life: 3000 }); -}; - -// Download file -const downloadFile = (file) => { - toast.add({ severity: 'info', summary: '下载', detail: `下载 ${file.name}`, life: 3000 }); -}; - -// Delete file -const confirmDelete = (file) => { - confirm.require({ - message: `您确定要删除 ${file.name} 吗?`, - header: '确认删除', - icon: 'pi pi-exclamation-triangle', - acceptClass: 'p-button-danger', - accept: () => { - // In a real app, you would call an API to delete the file - mediaFiles.value = mediaFiles.value.filter(f => f.id !== file.id); - toast.add({ severity: 'success', summary: '成功', detail: '文件已删除', life: 3000 }); - } - }); -}; - // Fetch media data const fetchMediaFiles = async () => { loading.value = true; @@ -214,7 +86,6 @@ const onPage = (event) => { onMounted(() => { fetchMediaFiles(); - getOssToken(); }); // File type badge severity mapping @@ -349,38 +220,6 @@ const formatFileSize = (bytes) => { - - - -
- - - - - -
-
正在上传: {{ currentFile?.name }}
-
-
-
-
{{ Math.round(uploadProgress) }}%
-
-
- - -
diff --git a/frontend/admin/src/router.js b/frontend/admin/src/router.js index 1c83da5..c341d6b 100644 --- a/frontend/admin/src/router.js +++ b/frontend/admin/src/router.js @@ -12,6 +12,11 @@ const routes = [ name: 'Medias', component: () => import('./pages/MediaPage.vue') }, + { + path: '/medias/uploads', + name: 'Uploads', + component: () => import('./pages/MediaUploadPage.vue') + }, { path: '/posts', name: 'Posts',