page for customer event rank
This commit is contained in:
@@ -74,14 +74,22 @@
|
||||
<h2 v-text="item.title" />
|
||||
<div :ref="element => setChartRef(item.id, element)" class="chart" />
|
||||
</section>
|
||||
<div v-if="!selectedCustomer" class="rank-summary" v-text="rankSummaryText"></div>
|
||||
<pull-down-refresh
|
||||
v-if="!selectedCustomer"
|
||||
v-model="rankRefreshing"
|
||||
class="rank-refresh"
|
||||
:refresh-timeout="30000"
|
||||
@refresh="refreshCustomerRegistrationRank"
|
||||
>
|
||||
<div class="rank-content">
|
||||
<div class="rank-summary" v-text="rankSummaryText"></div>
|
||||
<t-cell-group
|
||||
v-if="!selectedCustomer && customerRegistrationRankList.length"
|
||||
v-if="customerRegistrationRankList.length"
|
||||
class="rank-list"
|
||||
theme="card"
|
||||
>
|
||||
<t-cell
|
||||
v-for="item in customerRegistrationRankList"
|
||||
v-for="item in paginatedCustomerRegistrationRankList"
|
||||
:key="item.customerId"
|
||||
class="item"
|
||||
arrow
|
||||
@@ -101,7 +109,7 @@
|
||||
</span>
|
||||
</template>
|
||||
<template #title>
|
||||
<t-tag theme="primary" v-text="`No.${item.rank}`" />
|
||||
<t-tag theme="primary" :content="`No.${item.rank}`" />
|
||||
<span class="word-space" v-text="item.customerName || '未知客户'" />
|
||||
</template>
|
||||
<template #description>
|
||||
@@ -116,6 +124,23 @@
|
||||
</t-cell>
|
||||
</t-cell-group>
|
||||
<div v-else class="empty-block" v-text="rankLoading ? '正在加载排行榜' : '暂无登记排行数据'" />
|
||||
<div v-if="rankPaginationVisible" class="rank-pagination">
|
||||
<t-button
|
||||
variant="outline"
|
||||
size="small"
|
||||
:disabled="rankPageIndex < 2"
|
||||
@click="changeRankPage(-1)"
|
||||
>上一页</t-button>
|
||||
<span class="page" v-text="`第 ${rankPageIndex} / ${rankPageCount} 页`" />
|
||||
<t-button
|
||||
variant="outline"
|
||||
size="small"
|
||||
:disabled="rankPageCount < rankPageIndex + 1"
|
||||
@click="changeRankPage(1)"
|
||||
>下一页</t-button>
|
||||
</div>
|
||||
</div>
|
||||
</pull-down-refresh>
|
||||
<customer-selector
|
||||
v-model="selectedCustomerId"
|
||||
v-model:visible="selectorVisible"
|
||||
@@ -146,7 +171,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
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<RegistrationEventRecord[]>([]);
|
||||
const calendarLoading = ref(false);
|
||||
const customerRegistrationRankList = ref<CustomerRegistrationRankView[]>([]);
|
||||
const rankPageSize = 10;
|
||||
const rankPageIndex = ref(1);
|
||||
const rankDateFilterType = ref<RankDateFilterType>("all");
|
||||
const rankDateRange = ref(buildRankPresetRange("all"));
|
||||
const rankRegistrationEventIdList = ref<string[]>([]);
|
||||
const rankLoading = ref(false);
|
||||
const rankRefreshing = ref(false);
|
||||
const rankStartPickerVisible = ref(false);
|
||||
const rankEndPickerVisible = ref(false);
|
||||
const rankCache = new Map<string, CustomerRegistrationRankView[]>();
|
||||
const rankPendingRequestMap = new Map<string, Promise<CustomerRegistrationRankView[]>>();
|
||||
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,19 +509,68 @@ async function loadMonthRecords(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCustomerRegistrationRank(): Promise<void> {
|
||||
async function loadCustomerRegistrationRank(force = false): Promise<void> {
|
||||
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 {
|
||||
if (rankPendingRequestMap.get(cacheKey) === request) {
|
||||
rankPendingRequestMap.delete(cacheKey);
|
||||
}
|
||||
if (token === rankRequestToken) {
|
||||
rankLoading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function reloadCustomerRegistrationRank(): Promise<void> {
|
||||
await loadCustomerRegistrationRank();
|
||||
}
|
||||
|
||||
async function refreshCustomerRegistrationRank(): Promise<void> {
|
||||
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<void> {
|
||||
if (Array.isArray(value)) {
|
||||
return;
|
||||
@@ -738,6 +824,15 @@ function buildRankStatReq(): Partial<RegistrationEventRecordRangeReq> | 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<RankDateFilterType, "custom">): [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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -154,7 +154,7 @@ const menuGroupList = computed<MenuGroup[]>(() => [
|
||||
visible: userStore.hasPermissions(gaoPermissionCodes.qrCode.create)
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
]);
|
||||
|
||||
function buildFallbackAvatar(name: string): string {
|
||||
|
||||
Reference in New Issue
Block a user