diff --git a/backend/internal/modules/order/dto.go b/backend/internal/modules/order/dto.go index 352594f..e00cb02 100644 --- a/backend/internal/modules/order/dto.go +++ b/backend/internal/modules/order/dto.go @@ -72,6 +72,13 @@ type AdminActionRequest struct { type AuditMeta = auditlog.Meta +type PaginatedResult struct { + Items []OrderDTO `json:"items"` + Total int64 `json:"total"` + Page int `json:"page"` + PageSize int `json:"page_size"` +} + type RefundStatusDTO struct { OrderID uint64 `json:"order_id"` OrderNo string `json:"order_no"` diff --git a/backend/internal/modules/order/handler.go b/backend/internal/modules/order/handler.go index c6eceda..32f3133 100644 --- a/backend/internal/modules/order/handler.go +++ b/backend/internal/modules/order/handler.go @@ -53,12 +53,13 @@ func (h *Handler) List(c *gin.Context) { } func (h *Handler) AdminList(c *gin.Context) { - items, err := h.service.ListAdmin() + page, pageSize := parsePagination(c) + result, err := h.service.ListAdmin(page, pageSize) if err != nil { writeOrderError(c, err) return } - response.OK(c, gin.H{"items": items}) + response.OK(c, result) } func (h *Handler) AdminDetail(c *gin.Context) { @@ -408,6 +409,21 @@ func parseID(c *gin.Context) (uint64, bool) { return id, true } +func parsePagination(c *gin.Context) (int, int) { + page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) + pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) + if page < 1 { + page = 1 + } + if pageSize < 1 { + pageSize = 20 + } + if pageSize > 100 { + pageSize = 100 + } + return page, pageSize +} + func writeOrderError(c *gin.Context, err error) { switch { case errors.Is(err, ErrDependencyUnavailable): diff --git a/backend/internal/modules/order/repository.go b/backend/internal/modules/order/repository.go index 2c05994..59d13ae 100644 --- a/backend/internal/modules/order/repository.go +++ b/backend/internal/modules/order/repository.go @@ -707,20 +707,34 @@ func (r *Repository) ListForUser(userID uint64) ([]OrderDTO, error) { return items, nil } -func (r *Repository) ListAdmin() ([]OrderDTO, error) { +func (r *Repository) ListAdmin(page, pageSize int) (*PaginatedResult, error) { + var total int64 + if err := r.adminQuery().Count(&total).Error; err != nil { + return nil, err + } + + offset := (page - 1) * pageSize var rows []orderRow err := r.adminQuery(). Order("o.id DESC"). - Limit(200). + Limit(pageSize). + Offset(offset). Scan(&rows).Error if err != nil { return nil, err } + items := make([]OrderDTO, 0, len(rows)) for _, row := range rows { items = append(items, row.toAdminDTO()) } - return items, nil + + return &PaginatedResult{ + Items: items, + Total: total, + Page: page, + PageSize: pageSize, + }, nil } func (r *Repository) FindAdmin(orderID uint64) (*OrderDTO, error) { diff --git a/backend/internal/modules/order/service.go b/backend/internal/modules/order/service.go index 13bb602..9bb001c 100644 --- a/backend/internal/modules/order/service.go +++ b/backend/internal/modules/order/service.go @@ -141,11 +141,11 @@ func (s *Service) ListForUser(userID uint64) ([]OrderDTO, error) { return s.repo.ListForUser(userID) } -func (s *Service) ListAdmin() ([]OrderDTO, error) { +func (s *Service) ListAdmin(page, pageSize int) (*PaginatedResult, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } - return s.repo.ListAdmin() + return s.repo.ListAdmin(page, pageSize) } func (s *Service) FindAdmin(orderID uint64) (*OrderDTO, error) { diff --git a/frontend/src/features/admin/components/AdminTablePagination.vue b/frontend/src/features/admin/components/AdminTablePagination.vue new file mode 100644 index 0000000..a376f0d --- /dev/null +++ b/frontend/src/features/admin/components/AdminTablePagination.vue @@ -0,0 +1,67 @@ + + + diff --git a/frontend/src/features/admin/views/AdminAnnouncementsView.vue b/frontend/src/features/admin/views/AdminAnnouncementsView.vue index 403a538..b3c7f68 100644 --- a/frontend/src/features/admin/views/AdminAnnouncementsView.vue +++ b/frontend/src/features/admin/views/AdminAnnouncementsView.vue @@ -15,6 +15,7 @@ import { import type { Announcement } from '@/features/announcement' import { formatDateTime } from '@/utils/time' import AnnouncementDialog from '../components/AnnouncementDialog.vue' +import AdminTablePagination from '../components/AdminTablePagination.vue' const loading = ref(false) const announcements = ref([]) @@ -146,9 +147,13 @@ function handleFilterChange() { loadAnnouncements() } -function handleSizeChange() { +async function handlePageChange() { + await loadAnnouncements() +} + +async function handleSizeChange() { currentPage.value = 1 - loadAnnouncements() + await loadAnnouncements() } function getCategoryLabel(category: string) { @@ -261,17 +266,14 @@ function getStatusType(status: string): '' | 'success' | 'info' | 'warning' { -
- -
+ ([]) @@ -45,9 +46,13 @@ function resetFilters() { void loadLogs() } -function handleSizeChange() { +async function handlePageChange() { + await loadLogs() +} + +async function handleSizeChange() { currentPage.value = 1 - loadLogs() + await loadLogs() } function actorName(row: AdminAuditLog) { @@ -141,17 +146,14 @@ function actionType(action: string) { -
- -
+
diff --git a/frontend/src/features/admin/views/AdminDisputesView.vue b/frontend/src/features/admin/views/AdminDisputesView.vue index 87d29aa..2bc4ab0 100644 --- a/frontend/src/features/admin/views/AdminDisputesView.vue +++ b/frontend/src/features/admin/views/AdminDisputesView.vue @@ -6,6 +6,7 @@ import { arbitrateDispute, fetchAdminDisputes, type Dispute } from '@/features/d import { fetchAdminFileBlob } from '@/shared/api/files' import { disputeStatusLabel } from '@/utils/statusLabels' import { formatDateTime } from '@/utils/time' +import AdminTablePagination from '../components/AdminTablePagination.vue' const loading = ref(false) const submitting = ref(false) @@ -32,9 +33,13 @@ async function loadDisputes() { } } -function handleSizeChange() { +async function handlePageChange() { + await loadDisputes() +} + +async function handleSizeChange() { currentPage.value = 1 - loadDisputes() + await loadDisputes() } function openArbitration(row: Dispute) { @@ -132,17 +137,14 @@ function readError(error: unknown, fallback: string) { -
- -
+
diff --git a/frontend/src/features/admin/views/AdminMgrUsersView.vue b/frontend/src/features/admin/views/AdminMgrUsersView.vue index e96e0a1..d6b9db7 100644 --- a/frontend/src/features/admin/views/AdminMgrUsersView.vue +++ b/frontend/src/features/admin/views/AdminMgrUsersView.vue @@ -8,6 +8,7 @@ import { formatDateTime } from '@/utils/time' import AdminUserDialog from '../components/AdminUserDialog.vue' import AssignRolesDialog from '../components/AssignRolesDialog.vue' +import AdminTablePagination from '../components/AdminTablePagination.vue' const showDialog = ref(false) const editingAdmin = ref(null) @@ -141,17 +142,14 @@ const statusLabel: Record = { -
- -
+ -import { computed, ref } from 'vue' +import { computed, onMounted, ref } from 'vue' import { fetchAdminOrders, type Order } from '@/features/orders' -import { useAdminTable } from '@/features/admin/composables/useAdminTable' import { handoffStatusLabel, orderStatusLabel, settlementStatusLabel } from '@/utils/statusLabels' import { formatDateTime } from '@/utils/time' +import AdminTablePagination from '../components/AdminTablePagination.vue' const status = ref('') +const loading = ref(false) +const orders = ref([]) +const total = ref(0) +const currentPage = ref(1) +const currentPageSize = ref(20) -const { loading, data: orders, load: loadOrders } = useAdminTable({ - fetchFn: fetchAdminOrders, - initialData: [], -}) +onMounted(loadOrders) + +async function loadOrders() { + loading.value = true + try { + const result = await fetchAdminOrders(currentPage.value, currentPageSize.value) + orders.value = result.items + total.value = result.total + } finally { + loading.value = false + } +} + +async function handlePageChange() { + await loadOrders() +} const filteredOrders = computed(() => { if (!status.value) return orders.value @@ -74,5 +91,14 @@ const filteredOrders = computed(() => { + + diff --git a/frontend/src/features/admin/views/AdminUsersView.vue b/frontend/src/features/admin/views/AdminUsersView.vue index a48189f..f014826 100644 --- a/frontend/src/features/admin/views/AdminUsersView.vue +++ b/frontend/src/features/admin/views/AdminUsersView.vue @@ -6,6 +6,7 @@ import { fetchAdminUsers, freezeAdminUser, unfreezeAdminUser, type AdminUserItem import { useAdminPaginatedTable } from '@/features/admin/composables/useAdminPaginatedTable' import { userStatusLabel } from '@/utils/statusLabels' import { formatDateTime } from '@/utils/time' +import AdminTablePagination from '../components/AdminTablePagination.vue' const submitting = ref(false) const activeUser = ref(null) @@ -88,17 +89,14 @@ function readError(error: unknown, fallback: string) { -
- -
+
diff --git a/frontend/src/features/admin/views/AdminWalletLedgerView.vue b/frontend/src/features/admin/views/AdminWalletLedgerView.vue index acec643..bdbd35e 100644 --- a/frontend/src/features/admin/views/AdminWalletLedgerView.vue +++ b/frontend/src/features/admin/views/AdminWalletLedgerView.vue @@ -5,6 +5,7 @@ import { computed, onMounted, reactive, ref } from 'vue' import { fetchAdminWalletLedger, type AdminWalletLedger } from '@/features/admin/api/adminWallet' import { balanceTypeLabel, ledgerDirectionLabel } from '@/utils/statusLabels' import { formatDateTime } from '@/utils/time' +import AdminTablePagination from '../components/AdminTablePagination.vue' const loading = ref(false) const ledger = ref([]) @@ -50,9 +51,13 @@ function resetFilters() { void loadLedger() } -function handleSizeChange() { +async function handlePageChange() { + await loadLedger() +} + +async function handleSizeChange() { currentPage.value = 1 - loadLedger() + await loadLedger() } function money(value: number) { @@ -149,16 +154,13 @@ function directionLabel(direction: string) { -
- -
+ diff --git a/frontend/src/features/orders/api/orders.ts b/frontend/src/features/orders/api/orders.ts index 3837fed..1b69e98 100644 --- a/frontend/src/features/orders/api/orders.ts +++ b/frontend/src/features/orders/api/orders.ts @@ -157,9 +157,11 @@ export async function fetchOrders() { return data.data.items } -export async function fetchAdminOrders() { - const { data } = await apiClient.get>('/admin/orders') - return data.data.items +export async function fetchAdminOrders(page = 1, pageSize = 20) { + const { data } = await apiClient.get>('/admin/orders', { + params: { page, page_size: pageSize }, + }) + return data.data } export async function fetchOrder(id: string | number) {