feat:【mall 商城】交易统计、商品统计【antd】30%:迁移完成
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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' });
|
||||||
|
|||||||
@@ -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 @AI:searchTimes;参考 /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="支付件数"
|
||||||
Reference in New Issue
Block a user