feat: update page list show

This commit is contained in:
yanghao05
2025-04-08 15:35:58 +08:00
parent 7d28dff65b
commit cf8bb5f192
13 changed files with 280 additions and 102 deletions

View File

@@ -1,4 +1,5 @@
<script setup>
import { mediaService } from '@/api/mediaService';
import { InputText } from 'primevue';
import Badge from 'primevue/badge';
import Button from 'primevue/button';
@@ -12,7 +13,7 @@ import ProgressSpinner from 'primevue/progressspinner';
import Toast from 'primevue/toast';
import { useConfirm } from 'primevue/useconfirm';
import { useToast } from 'primevue/usetoast';
import { onMounted, ref } from 'vue';
import { computed, onMounted, ref } from 'vue';
// Import useConfirm dynamically to avoid the error when the service is not provided
const confirm = useConfirm();
@@ -44,55 +45,23 @@ const mediaTypes = ref([
const selectedMediaType = ref(mediaTypes.value[0]);
const globalFilterValue = ref('');
const loading = ref(false);
const filters = ref({
global: { value: null, matchMode: 'contains' }
});
// Add pagination state
const first = ref(0);
const rows = ref(10);
const totalRecords = ref(0);
const currentPage = ref(1);
// Add computed for total pages
const totalPages = computed(() => {
return Math.ceil(totalRecords.value / rows.value);
});
// Sample data - in a real app, this would come from an API
const mediaFiles = ref([
{
id: 1,
fileName: 'sunset-beach.jpg',
uploadTime: 'Today, 10:30 AM',
fileSize: '2.4 MB',
fileType: 'Image',
thumbnailUrl: 'https://via.placeholder.com/300x225',
iconClass: 'pi-image'
},
{
id: 2,
fileName: 'presentation.pdf',
uploadTime: 'Yesterday, 3:45 PM',
fileSize: '4.8 MB',
fileType: 'PDF',
thumbnailUrl: null,
iconClass: 'pi-file-pdf'
},
{
id: 3,
fileName: 'promo_video.mp4',
uploadTime: 'Aug 28, 2023',
fileSize: '24.8 MB',
fileType: 'Video',
thumbnailUrl: null,
iconClass: 'pi-video'
},
{
id: 4,
fileName: 'report_q3.docx',
uploadTime: 'Aug 25, 2023',
fileSize: '1.2 MB',
fileType: 'Document',
thumbnailUrl: null,
iconClass: 'pi-file'
},
{
id: 5,
fileName: 'podcast_interview.mp3',
uploadTime: 'Aug 20, 2023',
fileSize: '18.5 MB',
fileType: 'Audio',
thumbnailUrl: null,
iconClass: 'pi-volume-up'
}
]);
const mediaFiles = ref([]);
// File upload handling
const onUpload = (event) => {
@@ -109,20 +78,18 @@ const onUpload = (event) => {
// Preview file
const previewFile = (file) => {
// In a real app, this would open a modal with a preview or navigate to a preview page
toast.add({ severity: 'info', summary: 'Preview', detail: `Previewing ${file.fileName}`, life: 3000 });
toast.add({ severity: 'info', summary: 'Preview', detail: `Previewing ${file.name}`, life: 3000 });
};
// Download file
const downloadFile = (file) => {
// In a real app, this would trigger a download
toast.add({ severity: 'info', summary: 'Download', detail: `Downloading ${file.fileName}`, life: 3000 });
toast.add({ severity: 'info', summary: 'Download', detail: `Downloading ${file.name}`, life: 3000 });
};
// Delete file
const confirmDelete = (file) => {
confirm.require({
message: `Are you sure you want to delete ${file.fileName}?`,
message: `Are you sure you want to delete ${file.name}?`,
header: 'Confirmation',
icon: 'pi pi-exclamation-triangle',
acceptClass: 'p-button-danger',
@@ -138,13 +105,13 @@ const confirmDelete = (file) => {
const fetchMediaFiles = async () => {
loading.value = true;
try {
// In a real app, this would be an API call
// const response = await mediaApi.getMediaFiles();
// mediaFiles.value = response.data;
// Simulate API delay
await new Promise(resolve => setTimeout(resolve, 500));
// Using sample data already defined above
const response = await mediaService.getMedias({
page: currentPage.value,
limit: rows.value
});
mediaFiles.value = response.items;
totalRecords.value = response.total;
console.log(totalRecords.value);
} catch (error) {
toast.add({ severity: 'error', summary: 'Error', detail: 'Failed to load media files', life: 3000 });
} finally {
@@ -152,6 +119,16 @@ const fetchMediaFiles = async () => {
}
};
// Add page change handler
const onPage = (event) => {
first.value = event.first;
rows.value = event.rows;
currentPage.value = Math.floor(event.first / event.rows) + 1;
console.log(event)
fetchMediaFiles();
};
onMounted(() => {
fetchMediaFiles();
});
@@ -173,6 +150,27 @@ const getFileIcon = (file) => {
if (file.thumbnailUrl) return null;
return `pi ${file.iconClass}`;
};
// Add helper function to format file size
const formatFileSize = (bytes) => {
if (bytes === 0) return '0 B';
const k = BigInt(1024);
const sizes = ['B', 'KB', 'MB', 'GB'];
// Convert input to BigInt
const bytesValue = BigInt(bytes);
// Calculate the appropriate unit
let i = 0;
let size = bytesValue;
while (size >= k && i < sizes.length - 1) {
size = size / k;
i++;
}
// Convert back to number and format with commas
return `${Number(size).toLocaleString('en-US')} ${sizes[i]}`;
};
</script>
<template>
@@ -193,13 +191,22 @@ const getFileIcon = (file) => {
<InputText v-model="globalFilterValue" placeholder="Search media..." class="flex-1" />
</div>
<DataTable v-model:filters="filters" :value="mediaFiles" :paginator="true" :rows="5"
<DataTable v-model:filters="filters" :value="mediaFiles" :paginator="true" v-model:first="first"
v-model:rows="rows" :totalRecords="totalRecords" @page="onPage"
paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport RowsPerPageDropdown"
:rowsPerPageOptions="[5, 10, 25]"
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries" :loading="loading"
dataKey="id" :globalFilterFields="['fileName', 'fileType']"
:filters="{ global: { value: globalFilterValue, matchMode: 'contains' } }" stripedRows removableSort
class="p-datatable-sm" responsiveLayout="scroll">
:rows-per-page-options="[10, 25, 50]"
current-page-report-template="Showing {first} to {last} of {totalRecords} entries" :lazy="true"
:show-current-page-report="true">
<template #paginatorLeft>
<div class="flex items-center">
Per Page: {{ rows }}
</div>
</template>
<template #paginatorRight>
<div class="flex items-center">
Page {{ currentPage }} of {{ totalPages }}
</div>
</template>
<template #empty>
<div class="text-center p-4">No media files found.</div>
</template>
@@ -210,28 +217,32 @@ const getFileIcon = (file) => {
</div>
</template>
<Column field="fileName" header="File Name" sortable>
<Column field="name" header="File Name" sortable>
<template #body="{ data }">
<div class="flex items-center">
<div v-if="data.thumbnailUrl" class="flex-shrink-0 h-10 w-10 mr-3">
<img class="h-10 w-10 object-cover rounded" :src="data.thumbnailUrl"
:alt="data.fileName">
<div v-if="data.thumbnail_url" class="flex-shrink-0 h-10 w-10 mr-3">
<img class="h-10 w-10 object-cover rounded" :src="data.thumbnail_url" :alt="data.name">
</div>
<div v-else
class="flex-shrink-0 h-10 w-10 mr-3 bg-gray-100 rounded flex items-center justify-center">
<i :class="getFileIcon(data)" class="text-2xl"></i>
</div>
<div class="text-sm font-medium text-gray-900">{{ data.fileName }}</div>
<div class="text-sm font-medium text-gray-900">{{ data.name }}</div>
</div>
</template>
</Column>
<Column field="uploadTime" header="Upload Time" sortable></Column>
<Column field="fileSize" header="File Size" sortable></Column>
<Column field="upload_time" header="Upload Time" sortable></Column>
<Column field="fileType" header="File Type" sortable>
<Column field="file_size" header="File Size" sortable>
<template #body="{ data }">
<Badge :value="data.fileType" :severity="getBadgeSeverity(data.fileType)" />
{{ formatFileSize(data.file_size) }}
</template>
</Column>
<Column field="media_type" header="File Type" sortable>
<template #body="{ data }">
<Badge :value="data.file_type" :severity="getBadgeSeverity(data.file_type)" />
</template>
<template #filter="{ filterModel, filterCallback }">
<Dropdown v-model="filterModel.value" @change="filterCallback()" :options="mediaTypes"