Files
gao-customer-h5/src/pages/store/component/StoreChartSummary.vue
T
2026-08-24 18:32:48 +08:00

202 lines
5.3 KiB
Vue

<template>
<section class="summary">
<div v-for="item in summaryList" :key="item.label" class="item">
<span v-text="item.label" />
<strong v-text="item.value" />
</div>
</section>
</template>
<script setup lang="ts">
import { Time } from "timi-web";
import StoreChartAPI from "@/api/StoreChartAPI";
import type { CustomerDailyStatView } from "@/types/Customer";
import type { EventItem } from "@/types/EventItem";
import type { StoreEventRecordDailyStatView } from "@/types/Store";
interface SummaryItem {
label: string;
value: string;
}
const props = defineProps<{
dateTextList: string[];
}>();
// ---------- 摘要数据 ----------
const eventList = ref<EventItem[]>([]);
const customerDailyStatList = ref<CustomerDailyStatView[]>([]);
const recordDailyStatMap = ref<Record<string, StoreEventRecordDailyStatView[]>>({});
const todayDateValue = toDateValue(new Date());
const weekBeginDateValue = toDateValue(getWeekBeginDate(new Date()));
let summaryRequestId = 0;
const summaryList = computed<SummaryItem[]>(() => [
{
label: "当前客户",
value: `${getLatestTotalCustomerCount()} 人`
},
{
label: "本周新增",
value: `${sumCustomerNewCount()} 人`
},
{
label: "本周登记",
value: `${sumActionDailyCount()} 次`
}
]);
watch(() => props.dateTextList, async () => {
await loadSummaryData();
}, {
deep: true,
immediate: true
});
async function loadSummaryData(): Promise<void> {
if (!props.dateTextList.length) {
customerDailyStatList.value = [];
recordDailyStatMap.value = {};
return;
}
const requestId = ++summaryRequestId;
eventList.value = await StoreChartAPI.listValidEvents();
const eventIdList = getEventIdList();
const [customerStatList, recordDailyStatList] = await Promise.all([
StoreChartAPI.customerDailyStat({
beginAt: Time.toTimestamp(getDateStart(props.dateTextList[0])),
endAt: Time.toTimestamp(getDateEnd(props.dateTextList[props.dateTextList.length - 1]))
}),
eventIdList.length ? StoreChartAPI.eventRecordDailyStat({
beginAt: Time.toTimestamp(getDateStart(props.dateTextList[0])),
endAt: Time.toTimestamp(getDateEnd(props.dateTextList[props.dateTextList.length - 1])),
eventIdList
}) : Promise.resolve([])
]);
if (requestId === summaryRequestId) {
customerDailyStatList.value = customerStatList;
recordDailyStatMap.value = groupRecordDailyStatByEventId(recordDailyStatList);
}
}
// ---------- 统计 ----------
function getLatestTotalCustomerCount(): number {
const latest = customerDailyStatList.value[customerDailyStatList.value.length - 1];
return latest?.totalCustomerCount || 0;
}
function sumCustomerNewCount(): number {
return customerDailyStatList.value.filter(isRecentWeekStat).reduce((sum, item) => sum + (item.newCustomerCount || 0), 0);
}
function sumActionDailyCount(): number {
return eventList.value.reduce((sum, item) => sum + countRecentWeekRecordByEvent(item.id || ""), 0);
}
function countRecentWeekRecordByEvent(eventId: string): number {
return (recordDailyStatMap.value[eventId] || []).filter(isRecentWeekStat).reduce((sum, item) => sum + (item.dailyCount || 0), 0);
}
function isRecentWeekStat(item: { dateValue?: number }): boolean {
const dateValue = item.dateValue || 0;
return weekBeginDateValue <= dateValue && dateValue <= todayDateValue;
}
function getEventIdList(): string[] {
return eventList.value.map(item => item.id).filter((id): id is string => Boolean(id));
}
function groupRecordDailyStatByEventId(list: StoreEventRecordDailyStatView[]): Record<string, StoreEventRecordDailyStatView[]> {
const map: Record<string, StoreEventRecordDailyStatView[]> = {};
for (const item of list) {
if (!item.eventId) {
continue;
}
if (!map[item.eventId]) {
map[item.eventId] = [];
}
map[item.eventId].push(item);
}
return map;
}
// ---------- 日期 ----------
function getDateStart(dateText: string): Date;
function getDateStart(date: Date): Date;
function getDateStart(value: string | Date): Date {
if (typeof value !== "string") {
return new Date(value.getFullYear(), value.getMonth(), value.getDate(), 0, 0, 0, 0);
}
const [year, month, day] = value.split("-").map(Number);
return new Date(year, month - 1, day, 0, 0, 0, 0);
}
function getDateEnd(dateText: string): Date {
const [year, month, day] = dateText.split("-").map(Number);
return new Date(year, month - 1, day, 23, 59, 59, 999);
}
function getWeekBeginDate(date: Date): Date {
const value = getDateStart(date);
const day = value.getDay() || 7;
value.setDate(value.getDate() - day + 1);
return value;
}
function toDateValue(date: Date): number {
return Number(Time.formatDate(date).replaceAll("-", ""));
}
</script>
<style scoped lang="less">
.summary {
gap: .75rem;
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
.item {
padding: .875rem .625rem;
display: flex;
min-width: 0;
background: rgba(255, 255, 255, .92);
box-shadow: var(--td-shadow-1);
align-items: center;
border-radius: 1rem;
flex-direction: column;
span {
color: #52607a;
font-size: .75rem;
}
strong {
margin-top: .375rem;
color: #0f172a;
font-size: 1.125rem;
font-weight: 700;
}
}
}
:global(html.theme-dark) {
.summary {
.item {
background: rgba(15, 23, 42, .88);
box-shadow: 0 .75rem 2rem rgba(2, 6, 23, .32);
span {
color: #94a3b8;
}
strong {
color: #e2e8f0;
}
}
}
}
</style>