add product sku

This commit is contained in:
Timi
2026-09-07 12:59:27 +08:00
parent 9841723177
commit 1d70aebd83
49 changed files with 3700 additions and 756 deletions
@@ -0,0 +1,223 @@
<template>
<section class="order-charts">
<div class="section-heading">
<div>
<div class="eyebrow" v-text="'订单经营分析'" />
<h2 v-text="'客户订单看板'" />
</div>
<span v-text="'金额单位:元,数量单位:基础单位'" />
</div>
<order-daily-chart
:date-text-list="dateTextList"
:list="dailyList"
:loading="isLoading"
/>
<div class="two-columns">
<customer-order-product-rank-chart
:list="productRankList"
:loading="isLoading"
/>
<customer-order-sku-rank-chart
:list="skuRankList"
:loading="isLoading"
/>
</div>
<customer-consumption-daily-chart
:date-text-list="dateTextList"
:list="consumptionDailyList"
:loading="isLoading"
/>
<customer-order-calendar-chart
:date-text-list="dateTextList"
:list="calendarList"
:loading="isLoading"
/>
<div class="two-columns">
<customer-repeat-daily-chart
:date-text-list="dateTextList"
:list="repeatDailyList"
:loading="isLoading"
/>
<customer-repeat-interval-chart
:list="repeatIntervalList"
:loading="isLoading"
/>
</div>
</section>
</template>
<script setup lang="ts">
import GaoSalesOrderChartAPI from "@/api/GaoSalesOrderChartAPI";
import CustomerConsumptionDailyChart from "@/pages/customer/component/CustomerConsumptionDailyChart.vue";
import CustomerOrderCalendarChart from "@/pages/customer/component/CustomerOrderCalendarChart.vue";
import CustomerOrderProductRankChart from "@/pages/customer/component/CustomerOrderProductRankChart.vue";
import CustomerOrderSkuRankChart from "@/pages/customer/component/CustomerOrderSkuRankChart.vue";
import CustomerRepeatDailyChart from "@/pages/customer/component/CustomerRepeatDailyChart.vue";
import CustomerRepeatIntervalChart from "@/pages/customer/component/CustomerRepeatIntervalChart.vue";
import OrderDailyChart from "@/pages/product/component/OrderDailyChart.vue";
import type {
GaoCustomerOrderProductRankView,
GaoCustomerOrderSkuRankView,
GaoCustomerRepeatDailyStatView,
GaoCustomerRepeatIntervalStatView,
GaoSalesOrderCalendarStatView,
GaoSalesOrderConsumptionDailyStatView,
GaoSalesOrderDailyStatView
} from "@/types/GaoChart";
import { getErrorMessage } from "@/utils/error";
import { Message } from "@/utils/message";
defineOptions({
name: "CustomerOrderCharts"
});
const props = defineProps<{
customerId: string;
dateTextList: string[];
storeId?: string;
}>();
const dailyList = ref<GaoSalesOrderDailyStatView[]>([]);
const productRankList = ref<GaoCustomerOrderProductRankView[]>([]);
const skuRankList = ref<GaoCustomerOrderSkuRankView[]>([]);
const consumptionDailyList = ref<GaoSalesOrderConsumptionDailyStatView[]>([]);
const calendarList = ref<GaoSalesOrderCalendarStatView[]>([]);
const repeatDailyList = ref<GaoCustomerRepeatDailyStatView[]>([]);
const repeatIntervalList = ref<GaoCustomerRepeatIntervalStatView[]>([]);
const isLoading = ref(false);
let requestId = 0;
watch([
() => props.customerId,
() => props.storeId,
() => props.dateTextList
], async () => {
await loadCharts();
}, {
deep: true,
immediate: true
});
async function loadCharts(): Promise<void> {
if (!props.customerId || !props.dateTextList.length) {
clearData();
return;
}
const currentRequestId = ++requestId;
isLoading.value = true;
try {
const request = {
storeId: props.storeId,
customerId: props.customerId,
beginAt: getDateStart(props.dateTextList[0]),
endAt: getDateEnd(props.dateTextList[props.dateTextList.length - 1]),
topN: 10
};
const [daily, productRank, skuRank, consumptionDaily, calendar, repeatDaily, repeatInterval] = await Promise.all([
GaoSalesOrderChartAPI.daily(request),
GaoSalesOrderChartAPI.customerProductRank(request),
GaoSalesOrderChartAPI.customerSkuRank(request),
GaoSalesOrderChartAPI.consumptionDaily(request),
GaoSalesOrderChartAPI.calendar(request),
GaoSalesOrderChartAPI.repeatDaily(request),
GaoSalesOrderChartAPI.repeatInterval(request)
]);
if (currentRequestId !== requestId) {
return;
}
dailyList.value = daily || [];
productRankList.value = productRank || [];
skuRankList.value = skuRank || [];
consumptionDailyList.value = consumptionDaily || [];
calendarList.value = calendar || [];
repeatDailyList.value = repeatDaily || [];
repeatIntervalList.value = repeatInterval || [];
} catch (error) {
if (currentRequestId === requestId) {
Message.error(getErrorMessage(error, "客户订单图表加载失败"));
}
} finally {
if (currentRequestId === requestId) {
isLoading.value = false;
}
}
}
function clearData(): void {
isLoading.value = false;
dailyList.value = [];
productRankList.value = [];
skuRankList.value = [];
consumptionDailyList.value = [];
calendarList.value = [];
repeatDailyList.value = [];
repeatIntervalList.value = [];
}
function getDateStart(dateText: string): number {
const [year, month, day] = dateText.split("-").map(Number);
return new Date(year, month - 1, day, 0, 0, 0, 0).getTime();
}
function getDateEnd(dateText: string): number {
const [year, month, day] = dateText.split("-").map(Number);
return new Date(year, month - 1, day, 23, 59, 59, 999).getTime();
}
</script>
<style scoped lang="less">
.order-charts {
gap: 1rem;
display: flex;
flex-direction: column;
.section-heading {
gap: .5rem;
padding: 0 .25rem;
display: flex;
align-items: flex-end;
justify-content: space-between;
}
.eyebrow {
color: var(--td-brand-color);
font-size: .6875rem;
font-weight: 700;
letter-spacing: .12em;
}
h2 {
color: #0f172a;
margin: .25rem 0 0;
font-size: 1.125rem;
}
.section-heading > span {
color: #94a3b8;
font-size: .6875rem;
}
.two-columns {
gap: 1rem;
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
:global(html.theme-dark) {
.order-charts {
h2 {
color: #e2e8f0;
}
}
}
@media (max-width: 42rem) {
.order-charts {
.two-columns {
grid-template-columns: 1fr;
}
}
}
</style>