feat:【mall 商城】交易统计、商品统计【antd】20%:product-summary-card.vue 完善

pull/232/MERGE
YunaiV 2025-10-20 09:29:00 +08:00
parent b5bc8d24b9
commit 3aee960954
6 changed files with 37 additions and 45 deletions

View File

@ -41,14 +41,14 @@ export namespace MallProductStatisticsApi {
}
/** 获得商品统计分析 */
export function getProductStatisticsAnalyse(params: PageParam) {
export function getProductStatisticsAnalyse(params: any) {
return requestClient.get<
DataComparisonRespVO<MallProductStatisticsApi.ProductStatistics>
>('/statistics/product/analyse', { params });
}
/** 获得商品状况明细 */
export function getProductStatisticsList(params: PageParam) {
export function getProductStatisticsList(params: any) {
return requestClient.get<MallProductStatisticsApi.ProductStatistics[]>(
'/statistics/product/list',
{ params },
@ -56,7 +56,7 @@ export function getProductStatisticsList(params: PageParam) {
}
/** 导出获得商品状况明细 Excel */
export function exportProductStatisticsExcel(params: PageParam) {
export function exportProductStatisticsExcel(params: any) {
return requestClient.download('/statistics/product/export-excel', { params });
}

View File

@ -19,8 +19,8 @@ const times = ref<[Dayjs, Dayjs]>(); // 日期范围
const rangePickerProps = getRangePickerDefaultProps();
const timeRangeOptions = [
rangePickerProps.presets[3]!, //
rangePickerProps.presets[4]!, // 7
rangePickerProps.presets[5]!, // 30
rangePickerProps.presets[1]!, // 7
rangePickerProps.presets[2]!, // 30
];
const timeRangeType = ref(timeRangeOptions[1]!.label); //

View File

@ -11,9 +11,9 @@ import { onMounted, ref } from 'vue';
import { SummaryCard } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
import { fenToYuan } from '@vben/utils';
import { fenToYuan, isSameDay } from '@vben/utils';
import { Button, Card, Col, message, Row, Skeleton } from 'ant-design-vue';
import { Button, Card, Col, message, Row, Spin } from 'ant-design-vue';
import dayjs from 'dayjs';
import * as ProductStatisticsApi from '#/api/mall/statistics/product';
@ -24,12 +24,11 @@ import { getProductSummaryChartOptions } from './product-summary-chart-options';
/** 商品概况 */
defineOptions({ name: 'ProductSummaryCard' });
//
const trendLoading = ref(true); //
const exportLoading = ref(false); //
const trendSummary =
ref<DataComparisonRespVO<MallProductStatisticsApi.ProductStatistics>>(); //
// TODO @AIsearchTimes /Users/yunai/Java/yudao-ui-admin-vben-v5/apps/web-antd/src/views/mall/statistics/product/modules/rank-card.vue shortcutDateRangePicker
const shortcutDateRangePicker = ref();
const chartRef = ref<EchartsUIType>();
@ -39,7 +38,9 @@ const { renderEcharts } = useEcharts(chartRef);
const calculateRelativeRate = (value?: number, reference?: number): string => {
const refValue = Number(reference || 0);
const curValue = Number(value || 0);
if (!refValue || refValue === 0) return '0.00';
if (!refValue || refValue === 0) {
return '0.00';
}
return (((curValue - refValue) / refValue) * 100).toFixed(2);
};
@ -47,14 +48,12 @@ const calculateRelativeRate = (value?: number, reference?: number): string => {
const getProductTrendData = async (times?: [Dayjs, Dayjs]) => {
trendLoading.value = true;
try {
// : , 线,
let queryTimes = times;
if (!queryTimes && shortcutDateRangePicker.value?.times) {
queryTimes = shortcutDateRangePicker.value.times;
}
// 1. : , 线,
if (queryTimes && isSameDay(queryTimes[0], queryTimes[1])) {
//
queryTimes[0] = dayjs(queryTimes[0]).subtract(1, 'd');
}
@ -68,13 +67,9 @@ const getProductTrendData = async (times?: [Dayjs, Dayjs]) => {
}
};
/** 判断是否同一天 */
const isSameDay = (date1: Dayjs, date2: Dayjs): boolean => {
return date1.format('YYYY-MM-DD') === date2.format('YYYY-MM-DD');
};
/** 查询商品状况数据统计 */
const getProductTrendSummary = async (times?: [Dayjs, Dayjs]) => {
// TODO @AI queryTimes 使 searchTimes
const queryTimes = times
? [
times[0].format('YYYY-MM-DD HH:mm:ss'),
@ -88,6 +83,7 @@ const getProductTrendSummary = async (times?: [Dayjs, Dayjs]) => {
/** 查询商品状况数据列表 */
const getProductStatisticsList = async (times?: [Dayjs, Dayjs]) => {
// TODO @AI queryTimes 使 searchTimes
//
const queryTimes = times
? [
@ -98,18 +94,12 @@ const getProductStatisticsList = async (times?: [Dayjs, Dayjs]) => {
const list: MallProductStatisticsApi.ProductStatistics[] =
await ProductStatisticsApi.getProductStatisticsList({ times: queryTimes });
//
const processedList = list.map((item) => ({
...item,
orderPayPrice: Number(fenToYuan(item.orderPayPrice)),
afterSaleRefundPrice: Number(fenToYuan(item.afterSaleRefundPrice)),
}));
// Echarts
await renderEcharts(getProductSummaryChartOptions(processedList));
// Echarts
await renderEcharts(getProductSummaryChartOptions(list));
};
/** 导出按钮操作 */
// TODO @AI confirm
const handleExport = async () => {
try {
//
@ -175,7 +165,7 @@ onMounted(async () => {
<!-- 统计值 -->
<Row :gutter="16" class="mb-4">
<Col :xl="4" :md="8" :sm="24" class="mb-4">
<Col :xl="8" :md="8" :sm="24" class="mb-4">
<SummaryCard
title="商品浏览量"
tooltip="在选定条件下,所有商品详情页被访问的次数,一个人在统计时间内访问多次记为多次"
@ -192,8 +182,7 @@ onMounted(async () => {
"
/>
</Col>
<Col :xl="4" :md="8" :sm="24" class="mb-4">
<Col :xl="8" :md="8" :sm="24" class="mb-4">
<SummaryCard
title="商品访客数"
tooltip="在选定条件下,访问任何商品详情页的人数,一个人在统计时间范围内访问多次只记为一个"
@ -211,7 +200,7 @@ onMounted(async () => {
/>
</Col>
<Col :xl="4" :md="8" :sm="24" class="mb-4">
<Col :xl="8" :md="8" :sm="24" class="mb-4">
<SummaryCard
title="支付件数"
tooltip="在选定条件下,成功付款订单的商品件数之和"
@ -228,8 +217,7 @@ onMounted(async () => {
"
/>
</Col>
<Col :xl="4" :md="8" :sm="24" class="mb-4">
<Col :xl="8" :md="8" :sm="24" class="mb-4">
<SummaryCard
title="支付金额"
tooltip="在选定条件下,成功付款订单的商品金额之和"
@ -247,8 +235,7 @@ onMounted(async () => {
"
/>
</Col>
<Col :xl="4" :md="8" :sm="24" class="mb-4">
<Col :xl="8" :md="8" :sm="24" class="mb-4">
<SummaryCard
title="退款件数"
tooltip="在选定条件下,成功退款的商品件数之和"
@ -266,7 +253,7 @@ onMounted(async () => {
/>
</Col>
<Col :xl="4" :md="8" :sm="24" class="mb-4">
<Col :xl="8" :md="8" :sm="24" class="mb-4">
<SummaryCard
title="退款金额"
tooltip="在选定条件下,成功退款的商品金额之和"
@ -289,8 +276,8 @@ onMounted(async () => {
</Row>
<!-- 折线图 -->
<Skeleton :loading="trendLoading" :active="true">
<EchartsUI ref="chartRef" class="h-[500px]" />
</Skeleton>
<Spin :spinning="trendLoading">
<EchartsUI ref="chartRef" />
</Spin>
</Card>
</template>

View File

@ -1,7 +1,12 @@
import type { EChartsOption } from 'echarts';
/** 商品统计折线图配置 */
export function getProductSummaryChartOptions(data: any[]): EChartsOption {
export function getProductSummaryChartOptions(data: any[]): any {
// 处理数据:将金额从分转换为元
const processedData = data.map((item) => ({
...item,
orderPayPrice: Number((item.orderPayPrice / 100).toFixed(2)),
afterSaleRefundPrice: Number((item.afterSaleRefundPrice / 100).toFixed(2)),
}));
return {
dataset: {
dimensions: [
@ -11,7 +16,7 @@ export function getProductSummaryChartOptions(data: any[]): EChartsOption {
'orderPayPrice',
'afterSaleRefundPrice',
],
source: data,
source: processedData,
},
grid: {
left: 20,

View File

@ -119,7 +119,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
multiple: false,
},
toolbarConfig: {
refresh: false,
enabled: false,
},
} as VxeTableGridOptions,
});

View File

@ -23,4 +23,4 @@ export * from './window';
export { default as cloneDeep } from 'lodash.clonedeep';
export { default as get } from 'lodash.get';
export { default as isEqual } from 'lodash.isequal';
export { default as set } from 'lodash.set';
export { default as set } from 'lodash.set';