diff --git a/src/pages/management/CustomerDataCharts.vue b/src/pages/management/CustomerDataCharts.vue index 505f543..bf484e0 100644 --- a/src/pages/management/CustomerDataCharts.vue +++ b/src/pages/management/CustomerDataCharts.vue @@ -74,48 +74,73 @@

-
- - - - - - - -
+
+
+ + + + + + + +
+
+ 上一页 + + 下一页 +
+
+ import type {ECharts, EChartsOption} from "echarts"; -import type {DropdownValue} from "tdesign-mobile-vue"; +import {PullDownRefresh, type DropdownValue} from "tdesign-mobile-vue"; import {BarChart, LineChart} from "echarts/charts"; import {GridComponent, LegendComponent, TooltipComponent} from "echarts/components"; import {init, use} from "echarts/core"; @@ -215,12 +240,18 @@ const monthCursor = ref(getMonthStart(new Date())); const monthRecords = ref([]); const calendarLoading = ref(false); const customerRegistrationRankList = ref([]); +const rankPageSize = 10; +const rankPageIndex = ref(1); const rankDateFilterType = ref("all"); const rankDateRange = ref(buildRankPresetRange("all")); const rankRegistrationEventIdList = ref([]); const rankLoading = ref(false); +const rankRefreshing = ref(false); const rankStartPickerVisible = ref(false); const rankEndPickerVisible = ref(false); +const rankCache = new Map(); +const rankPendingRequestMap = new Map>(); +let rankRequestToken = 0; let latestDecodedText = ""; const registrationEventNameMap = computed(() => { @@ -305,6 +336,12 @@ const rankSummaryText = computed(() => { } return `范围:${rankDateFilterLabel.value}|${rankRegistrationEventFilterLabel.value}`; }); +const paginatedCustomerRegistrationRankList = computed(() => { + const startIndex = (rankPageIndex.value - 1) * rankPageSize; + return customerRegistrationRankList.value.slice(startIndex, startIndex + rankPageSize); +}); +const rankPageCount = computed(() => Math.max(1, Math.ceil(customerRegistrationRankList.value.length / rankPageSize))); +const rankPaginationVisible = computed(() => 1 < rankPageCount.value); watch(chartList, async () => { await nextTick(); @@ -472,12 +509,44 @@ async function loadMonthRecords(): Promise { } } -async function loadCustomerRegistrationRank(): Promise { +async function loadCustomerRegistrationRank(force = false): Promise { + const cacheKey = buildRankCacheKey(); + if (!force) { + const cachedList = rankCache.get(cacheKey); + if (cachedList) { + customerRegistrationRankList.value = cachedList; + rankPageIndex.value = 1; + return; + } + } else { + rankCache.delete(cacheKey); + } + + const token = rankRequestToken + 1; + rankRequestToken = token; rankLoading.value = true; + let request = force ? undefined : rankPendingRequestMap.get(cacheKey); + if (!request) { + request = RegistrationEventRecordAPI.customerRegistrationRank(buildRankStatReq()); + rankPendingRequestMap.set(cacheKey, request); + } try { - customerRegistrationRankList.value = await RegistrationEventRecordAPI.customerRegistrationRank(buildRankStatReq()); + const result = await request; + if (rankPendingRequestMap.get(cacheKey) === request) { + rankCache.set(cacheKey, result); + } + if (token !== rankRequestToken) { + return; + } + customerRegistrationRankList.value = result; + rankPageIndex.value = 1; } finally { - rankLoading.value = false; + if (rankPendingRequestMap.get(cacheKey) === request) { + rankPendingRequestMap.delete(cacheKey); + } + if (token === rankRequestToken) { + rankLoading.value = false; + } } } @@ -485,6 +554,23 @@ async function reloadCustomerRegistrationRank(): Promise { await loadCustomerRegistrationRank(); } +async function refreshCustomerRegistrationRank(): Promise { + rankRefreshing.value = true; + try { + await loadCustomerRegistrationRank(true); + } finally { + rankRefreshing.value = false; + } +} + +function changeRankPage(offset: number): void { + const nextPage = rankPageIndex.value + offset; + if (nextPage < 1 || rankPageCount.value < nextPage) { + return; + } + rankPageIndex.value = nextPage; +} + async function selectRankDateFilter(value: DropdownValue): Promise { if (Array.isArray(value)) { return; @@ -738,6 +824,15 @@ function buildRankStatReq(): Partial | undefine }; } +function buildRankCacheKey(): string { + const req = buildRankStatReq(); + return JSON.stringify({ + beginRegisteredAt: req?.beginRegisteredAt, + endRegisteredAt: req?.endRegisteredAt, + registrationEventIdList: req?.registrationEventIdList?.slice().sort() + }); +} + function buildRankPresetRange(type: Exclude): [string, string] { if (type === "all") { return ["", ""]; @@ -894,6 +989,12 @@ function parseRankDateTime(value: string, isEnd: boolean): Date { text-align: center; } + .rank-content { + gap: 1rem; + display: flex; + flex-direction: column; + } + .rank-list { margin: 0; @@ -934,6 +1035,18 @@ function parseRankDateTime(value: string, isEnd: boolean): Date { } } + .rank-pagination { + gap: .75rem; + display: flex; + align-items: center; + justify-content: center; + + .page { + color: #52607a; + font-size: .875rem; + } + } + .chart-section { h2 { margin-bottom: .875rem; @@ -1034,6 +1147,12 @@ function parseRankDateTime(value: string, isEnd: boolean): Date { background: rgba(30, 41, 59, .9); } + .rank-pagination { + .page { + color: #94a3b8; + } + } + } } diff --git a/src/pages/tabs/ManagementTab.vue b/src/pages/tabs/ManagementTab.vue index 0eee6e8..6fc3039 100644 --- a/src/pages/tabs/ManagementTab.vue +++ b/src/pages/tabs/ManagementTab.vue @@ -154,7 +154,7 @@ const menuGroupList = computed(() => [ visible: userStore.hasPermissions(gaoPermissionCodes.qrCode.create) } ] - }, + } ]); function buildFallbackAvatar(name: string): string {