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 {