feat:【mall 商城】交易统计、商品统计【antd】30%:迁移完成

This commit is contained in:
YunaiV
2025-10-20 09:39:41 +08:00
parent 3aee960954
commit b11b7582c2
4 changed files with 51 additions and 81 deletions

View File

@@ -39,7 +39,8 @@ async function getMemberSexStatisticsList() {
value: userCount || 0, value: userCount || 0,
}; };
}); });
// 更新 Echarts 数据
// 渲染图表
await renderEcharts(getSexChartOptions(chartData)); await renderEcharts(getSexChartOptions(chartData));
} finally { } finally {
loading.value = false; loading.value = false;

View File

@@ -1,8 +1,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import { DocAlert, Page } from '@vben/common-ui'; import { DocAlert, Page } from '@vben/common-ui';
import ProductSummaryCard from './modules/product-summary-card.vue';
import ProductRankCard from './modules/rank-card.vue'; import ProductRankCard from './modules/rank-card.vue';
import ProductSummaryCard from './modules/summary-card.vue';
/** 商品统计 */ /** 商品统计 */
defineOptions({ name: 'ProductStatistics' }); defineOptions({ name: 'ProductStatistics' });

View File

@@ -6,20 +6,25 @@ import type { EchartsUIType } from '@vben/plugins/echarts';
import type { DataComparisonRespVO } from '#/api/mall/statistics/common'; import type { DataComparisonRespVO } from '#/api/mall/statistics/common';
import type { MallProductStatisticsApi } from '#/api/mall/statistics/product'; import type { MallProductStatisticsApi } from '#/api/mall/statistics/product';
import { onMounted, ref } from 'vue'; import { ref } from 'vue';
import { SummaryCard } from '@vben/common-ui'; import { confirm, SummaryCard } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons'; import { IconifyIcon } from '@vben/icons';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts'; import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
import { fenToYuan, isSameDay } from '@vben/utils'; import {
downloadFileFromBlobPart,
fenToYuan,
formatDateTime,
isSameDay,
} from '@vben/utils';
import { Button, Card, Col, message, Row, Spin } from 'ant-design-vue'; import { Button, Card, Col, Row, Spin } from 'ant-design-vue';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import * as ProductStatisticsApi from '#/api/mall/statistics/product'; import * as ProductStatisticsApi from '#/api/mall/statistics/product';
import ShortcutDateRangePicker from '#/components/shortcut-date-range-picker/shortcut-date-range-picker.vue'; import ShortcutDateRangePicker from '#/components/shortcut-date-range-picker/shortcut-date-range-picker.vue';
import { getProductSummaryChartOptions } from './product-summary-chart-options'; import { getProductSummaryChartOptions } from './summary-chart-options';
/** 商品概况 */ /** 商品概况 */
defineOptions({ name: 'ProductSummaryCard' }); defineOptions({ name: 'ProductSummaryCard' });
@@ -28,8 +33,7 @@ const trendLoading = ref(true); // 商品状态加载中
const exportLoading = ref(false); // const exportLoading = ref(false); //
const trendSummary = const trendSummary =
ref<DataComparisonRespVO<MallProductStatisticsApi.ProductStatistics>>(); // 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 searchTimes = ref<string[]>([]);
const shortcutDateRangePicker = ref();
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);
@@ -44,104 +48,73 @@ const calculateRelativeRate = (value?: number, reference?: number): string => {
return (((curValue - refValue) / refValue) * 100).toFixed(2); return (((curValue - refValue) / refValue) * 100).toFixed(2);
}; };
/** 处理日期范围变化 */
const handleDateRangeChange = (times?: [Dayjs, Dayjs]) => {
if (times?.length !== 2) {
getProductTrendData();
return;
}
// : , 线,
let adjustedTimes = times;
if (isSameDay(times[0], times[1])) {
adjustedTimes = [dayjs(times[0]).subtract(1, 'd'), times[1]];
}
searchTimes.value = [
formatDateTime(adjustedTimes[0]) as string,
formatDateTime(adjustedTimes[1]) as string,
];
//
getProductTrendData();
};
/** 处理商品状况查询 */ /** 处理商品状况查询 */
const getProductTrendData = async (times?: [Dayjs, Dayjs]) => { const getProductTrendData = async () => {
trendLoading.value = true; trendLoading.value = true;
try { try {
// : , 线, await Promise.all([getProductTrendSummary(), getProductStatisticsList()]);
let queryTimes = times;
if (!queryTimes && shortcutDateRangePicker.value?.times) {
queryTimes = shortcutDateRangePicker.value.times;
}
if (queryTimes && isSameDay(queryTimes[0], queryTimes[1])) {
queryTimes[0] = dayjs(queryTimes[0]).subtract(1, 'd');
}
//
await Promise.all([
getProductTrendSummary(queryTimes),
getProductStatisticsList(queryTimes),
]);
} finally { } finally {
trendLoading.value = false; trendLoading.value = false;
} }
}; };
/** 查询商品状况数据统计 */ /** 查询商品状况数据统计 */
const getProductTrendSummary = async (times?: [Dayjs, Dayjs]) => { async function getProductTrendSummary() {
// TODO @AI queryTimes 使 searchTimes
const queryTimes = times
? [
times[0].format('YYYY-MM-DD HH:mm:ss'),
times[1].format('YYYY-MM-DD HH:mm:ss'),
]
: undefined;
trendSummary.value = await ProductStatisticsApi.getProductStatisticsAnalyse({ trendSummary.value = await ProductStatisticsApi.getProductStatisticsAnalyse({
times: queryTimes, times: searchTimes.value.length > 0 ? searchTimes.value : undefined,
}); });
}; }
/** 查询商品状况数据列表 */ /** 查询商品状况数据列表 */
const getProductStatisticsList = async (times?: [Dayjs, Dayjs]) => { async function getProductStatisticsList() {
// TODO @AI queryTimes 使 searchTimes const list = await ProductStatisticsApi.getProductStatisticsList({
// times: searchTimes.value.length > 0 ? searchTimes.value : undefined,
const queryTimes = times });
? [
times[0].format('YYYY-MM-DD HH:mm:ss'),
times[1].format('YYYY-MM-DD HH:mm:ss'),
]
: undefined;
const list: MallProductStatisticsApi.ProductStatistics[] =
await ProductStatisticsApi.getProductStatisticsList({ times: queryTimes });
// Echarts //
await renderEcharts(getProductSummaryChartOptions(list)); await renderEcharts(getProductSummaryChartOptions(list));
}; }
/** 导出按钮操作 */ /** 导出按钮操作 */
// TODO @AI confirm async function handleExport() {
const handleExport = async () => {
try { try {
// //
await message.confirm({ await confirm({
content: '确认导出商品状况数据吗?', content: '确认导出商品状况数据吗?',
okText: '确定',
cancelText: '取消',
}); });
// //
exportLoading.value = true; exportLoading.value = true;
const times = shortcutDateRangePicker.value?.times;
const queryTimes = times
? [
times[0].format('YYYY-MM-DD HH:mm:ss'),
times[1].format('YYYY-MM-DD HH:mm:ss'),
]
: undefined;
const data = await ProductStatisticsApi.exportProductStatisticsExcel({ const data = await ProductStatisticsApi.exportProductStatisticsExcel({
times: queryTimes, times: searchTimes.value.length > 0 ? searchTimes.value : undefined,
}); });
// //
const blob = new Blob([data], { downloadFileFromBlobPart({ fileName: '商品状况.xlsx', source: data });
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
});
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = '商品状况.xlsx';
link.click();
window.URL.revokeObjectURL(url);
} catch { } catch {
// //
} finally { } finally {
exportLoading.value = false; exportLoading.value = false;
} }
}; }
/** 初始化 */
onMounted(async () => {
await getProductTrendData();
});
</script> </script>
<template> <template>
@@ -149,10 +122,7 @@ onMounted(async () => {
<template #extra> <template #extra>
<!-- 查询条件 --> <!-- 查询条件 -->
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<ShortcutDateRangePicker <ShortcutDateRangePicker @change="handleDateRangeChange">
ref="shortcutDateRangePicker"
@change="getProductTrendData"
>
<Button class="ml-4" @click="handleExport" :loading="exportLoading"> <Button class="ml-4" @click="handleExport" :loading="exportLoading">
<template #icon> <template #icon>
<IconifyIcon icon="lucide:download" /> <IconifyIcon icon="lucide:download" />
@@ -199,7 +169,6 @@ onMounted(async () => {
" "
/> />
</Col> </Col>
<Col :xl="8" :md="8" :sm="24" class="mb-4"> <Col :xl="8" :md="8" :sm="24" class="mb-4">
<SummaryCard <SummaryCard
title="支付件数" title="支付件数"