From d66b9f2dfa86af4d864c21d5d8306e8e093aca59 Mon Sep 17 00:00:00 2001 From: Rogee Date: Thu, 15 Jan 2026 10:27:03 +0800 Subject: [PATCH] feat: add report charts and drilldowns --- .../src/components/StatisticsStrip.vue | 12 +- .../src/views/superadmin/Contents.vue | 103 +++++++-- .../src/views/superadmin/Orders.vue | 102 +++++++-- .../src/views/superadmin/Reports.vue | 200 +++++++++++++++++- 4 files changed, 368 insertions(+), 49 deletions(-) diff --git a/frontend/superadmin/src/components/StatisticsStrip.vue b/frontend/superadmin/src/components/StatisticsStrip.vue index fcd17a7..de937a6 100644 --- a/frontend/superadmin/src/components/StatisticsStrip.vue +++ b/frontend/superadmin/src/components/StatisticsStrip.vue @@ -14,7 +14,15 @@ const props = defineProps({ diff --git a/frontend/superadmin/src/views/superadmin/Contents.vue b/frontend/superadmin/src/views/superadmin/Contents.vue index 2b0b6e9..7bdb169 100644 --- a/frontend/superadmin/src/views/superadmin/Contents.vue +++ b/frontend/superadmin/src/views/superadmin/Contents.vue @@ -3,9 +3,11 @@ import SearchField from '@/components/SearchField.vue'; import SearchPanel from '@/components/SearchPanel.vue'; import { ContentService } from '@/service/ContentService'; import { useToast } from 'primevue/usetoast'; -import { computed, onMounted, ref } from 'vue'; +import { computed, ref, watch } from 'vue'; +import { useRoute } from 'vue-router'; const toast = useToast(); +const route = useRoute(); const loading = ref(false); const contents = ref([]); @@ -47,6 +49,24 @@ const visibilityOptions = [ { label: 'private', value: 'private' } ]; +function getQueryValue(value) { + if (Array.isArray(value)) return value[0]; + return value ?? null; +} + +function parseNumber(value) { + const parsed = Number(value); + if (!Number.isFinite(parsed)) return null; + return parsed; +} + +function parseDate(value) { + if (!value) return null; + const date = new Date(value); + if (Number.isNaN(date.getTime())) return null; + return date; +} + function formatDate(value) { if (!value) return '-'; if (String(value).startsWith('0001-01-01')) return '-'; @@ -55,6 +75,56 @@ function formatDate(value) { return date.toLocaleString(); } +function resetFilters() { + contentID.value = null; + tenantID.value = null; + tenantCode.value = ''; + tenantName.value = ''; + ownerUserID.value = null; + ownerUsername.value = ''; + keyword.value = ''; + status.value = ''; + visibility.value = ''; + publishedAtFrom.value = null; + publishedAtTo.value = null; + createdAtFrom.value = null; + createdAtTo.value = null; + priceAmountMin.value = null; + priceAmountMax.value = null; + sortField.value = 'id'; + sortOrder.value = -1; +} + +function applyRouteQuery(query) { + resetFilters(); + + const idValue = getQueryValue(query?.id); + const tenantValue = getQueryValue(query?.tenant_id); + const userValue = getQueryValue(query?.user_id); + + if (idValue) contentID.value = parseNumber(idValue); + if (tenantValue) tenantID.value = parseNumber(tenantValue); + if (userValue) ownerUserID.value = parseNumber(userValue); + + const statusValue = getQueryValue(query?.status); + const visibilityValue = getQueryValue(query?.visibility); + const keywordValue = getQueryValue(query?.keyword); + + if (statusValue !== null) status.value = String(statusValue); + if (visibilityValue !== null) visibility.value = String(visibilityValue); + if (keywordValue !== null) keyword.value = String(keywordValue); + + const publishedFromValue = getQueryValue(query?.published_at_from); + const publishedToValue = getQueryValue(query?.published_at_to); + const createdFromValue = getQueryValue(query?.created_at_from); + const createdToValue = getQueryValue(query?.created_at_to); + + if (publishedFromValue) publishedAtFrom.value = parseDate(publishedFromValue); + if (publishedToValue) publishedAtTo.value = parseDate(publishedToValue); + if (createdFromValue) createdAtFrom.value = parseDate(createdFromValue); + if (createdToValue) createdAtTo.value = parseDate(createdToValue); +} + function formatCny(amountInCents) { const amount = Number(amountInCents) / 100; if (!Number.isFinite(amount)) return '-'; @@ -128,23 +198,7 @@ function onSearch() { } function onReset() { - contentID.value = null; - tenantID.value = null; - tenantCode.value = ''; - tenantName.value = ''; - ownerUserID.value = null; - ownerUsername.value = ''; - keyword.value = ''; - status.value = ''; - visibility.value = ''; - publishedAtFrom.value = null; - publishedAtTo.value = null; - createdAtFrom.value = null; - createdAtTo.value = null; - priceAmountMin.value = null; - priceAmountMax.value = null; - sortField.value = 'id'; - sortOrder.value = -1; + resetFilters(); page.value = 1; rows.value = 10; loadContents(); @@ -192,9 +246,16 @@ async function confirmUnpublish() { } } -onMounted(() => { - loadContents(); -}); +watch( + () => route.query, + (query) => { + applyRouteQuery(query); + page.value = 1; + rows.value = 10; + loadContents(); + }, + { immediate: true } +);