<script lang="ts" setup>
|
import type { Dayjs } from 'dayjs';
|
|
import type { EchartsUIType } from '..\..\..\..\..\packages\effects\plugins\src\echarts';
|
|
import type { DataComparisonRespVO } from '#/api/mall/statistics/common';
|
import type { MallProductStatisticsApi } from '#/api/mall/statistics/product';
|
|
import { ref } from 'vue';
|
|
import { confirm, SummaryCard } from '..\..\..\..\..\packages\effects\common-ui\src';
|
import { IconifyIcon } from '..\..\..\..\..\packages\icons\src';
|
import { EchartsUI, useEcharts } from '..\..\..\..\..\packages\effects\plugins\src\echarts';
|
import {
|
downloadFileFromBlobPart,
|
fenToYuan,
|
formatDateTime,
|
isSameDay,
|
} from '..\..\..\..\..\packages\utils\src';
|
|
import { Button, Card, Col, Row, Spin } from 'ant-design-vue';
|
import dayjs from 'dayjs';
|
|
import {
|
exportProductStatisticsExcel,
|
getProductStatisticsAnalyse,
|
getProductStatisticsList,
|
} from '#/api/mall/statistics/product';
|
import ShortcutDateRangePicker from '#/components/shortcut-date-range-picker/shortcut-date-range-picker.vue';
|
import { $t } from '#/locales';
|
|
import { getProductSummaryChartOptions } from './summary-chart-options';
|
|
/** 商品概况 */
|
defineOptions({ name: 'ProductSummaryCard' });
|
|
const trendLoading = ref(true); // 商品状态加载中
|
const exportLoading = ref(false); // 导出的加载中
|
const trendSummary =
|
ref<DataComparisonRespVO<MallProductStatisticsApi.ProductStatisticsRespVO>>(); // 商品状况统计数据
|
const searchTimes = ref<string[]>([]);
|
|
const chartRef = ref<EchartsUIType>();
|
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';
|
}
|
return (((curValue - refValue) / refValue) * 100).toFixed(2);
|
};
|
|
/** 处理日期范围变化 */
|
const handleDateRangeChange = (times?: [Dayjs, Dayjs]) => {
|
if (times?.length !== 2) {
|
loadProductTrendData();
|
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,
|
];
|
|
// 查询数据
|
loadProductTrendData();
|
};
|
|
/** 处理商品状况查询 */
|
const loadProductTrendData = async () => {
|
trendLoading.value = true;
|
try {
|
await Promise.all([loadProductTrendSummary(), loadProductStatisticsList()]);
|
} finally {
|
trendLoading.value = false;
|
}
|
};
|
|
/** 查询商品状况数据统计 */
|
async function loadProductTrendSummary() {
|
trendSummary.value = await getProductStatisticsAnalyse({
|
times: searchTimes.value.length > 0 ? searchTimes.value : undefined,
|
});
|
}
|
|
/** 查询商品状况数据列表 */
|
async function loadProductStatisticsList() {
|
const list = await getProductStatisticsList({
|
times: searchTimes.value.length > 0 ? searchTimes.value : undefined,
|
});
|
|
// 渲染图表
|
await renderEcharts(getProductSummaryChartOptions(list));
|
}
|
|
/** 导出按钮操作 */
|
async function handleExport() {
|
try {
|
// 导出的二次确认
|
await confirm('确认导出商品状况数据吗?');
|
// 发起导出
|
exportLoading.value = true;
|
const data = await exportProductStatisticsExcel({
|
times: searchTimes.value.length > 0 ? searchTimes.value : undefined,
|
});
|
// 处理下载
|
downloadFileFromBlobPart({ fileName: '商品状况.xlsx', source: data });
|
} finally {
|
exportLoading.value = false;
|
}
|
}
|
</script>
|
|
<template>
|
<Card :bordered="false" title="商品概况" class="h-full">
|
<template #extra>
|
<!-- 查询条件 -->
|
<div class="flex items-center gap-2">
|
<ShortcutDateRangePicker @change="handleDateRangeChange">
|
<Button class="ml-4" @click="handleExport" :loading="exportLoading">
|
<template #icon>
|
<IconifyIcon icon="lucide:download" />
|
</template>
|
{{ $t('page.action.export') }}
|
</Button>
|
</ShortcutDateRangePicker>
|
</div>
|
</template>
|
|
<!-- 统计值 -->
|
<Row :gutter="16" class="mb-4">
|
<Col :xl="8" :md="8" :sm="24" class="mb-4">
|
<SummaryCard
|
title="商品浏览量"
|
tooltip="在选定条件下,所有商品详情页被访问的次数,一个人在统计时间内访问多次记为多次"
|
icon="lucide:eye"
|
icon-color="text-blue-500"
|
icon-bg-color="bg-blue-100"
|
:decimals="0"
|
:value="trendSummary?.value?.browseCount || 0"
|
:percent="
|
calculateRelativeRate(
|
trendSummary?.value?.browseCount,
|
trendSummary?.reference?.browseCount,
|
)
|
"
|
/>
|
</Col>
|
<Col :xl="8" :md="8" :sm="24" class="mb-4">
|
<SummaryCard
|
title="商品访客数"
|
tooltip="在选定条件下,访问任何商品详情页的人数,一个人在统计时间范围内访问多次只记为一个"
|
icon="lucide:users"
|
icon-color="text-purple-500"
|
icon-bg-color="bg-purple-100"
|
:decimals="0"
|
:value="trendSummary?.value?.browseUserCount || 0"
|
:percent="
|
calculateRelativeRate(
|
trendSummary?.value?.browseUserCount,
|
trendSummary?.reference?.browseUserCount,
|
)
|
"
|
/>
|
</Col>
|
<Col :xl="8" :md="8" :sm="24" class="mb-4">
|
<SummaryCard
|
title="支付件数"
|
tooltip="在选定条件下,成功付款订单的商品件数之和"
|
icon="lucide:credit-card"
|
icon-color="text-yellow-500"
|
icon-bg-color="bg-yellow-100"
|
:decimals="0"
|
:value="trendSummary?.value?.orderPayCount || 0"
|
:percent="
|
calculateRelativeRate(
|
trendSummary?.value?.orderPayCount,
|
trendSummary?.reference?.orderPayCount,
|
)
|
"
|
/>
|
</Col>
|
<Col :xl="8" :md="8" :sm="24" class="mb-4">
|
<SummaryCard
|
title="支付金额"
|
tooltip="在选定条件下,成功付款订单的商品金额之和"
|
icon="lucide:banknote"
|
icon-color="text-green-500"
|
icon-bg-color="bg-green-100"
|
prefix="¥"
|
:decimals="2"
|
:value="Number(fenToYuan(trendSummary?.value?.orderPayPrice || 0))"
|
:percent="
|
calculateRelativeRate(
|
trendSummary?.value?.orderPayPrice,
|
trendSummary?.reference?.orderPayPrice,
|
)
|
"
|
/>
|
</Col>
|
<Col :xl="8" :md="8" :sm="24" class="mb-4">
|
<SummaryCard
|
title="退款件数"
|
tooltip="在选定条件下,成功退款的商品件数之和"
|
icon="lucide:wallet"
|
icon-color="text-cyan-500"
|
icon-bg-color="bg-cyan-100"
|
:decimals="0"
|
:value="trendSummary?.value?.afterSaleCount || 0"
|
:percent="
|
calculateRelativeRate(
|
trendSummary?.value?.afterSaleCount,
|
trendSummary?.reference?.afterSaleCount,
|
)
|
"
|
/>
|
</Col>
|
<Col :xl="8" :md="8" :sm="24" class="mb-4">
|
<SummaryCard
|
title="退款金额"
|
tooltip="在选定条件下,成功退款的商品金额之和"
|
icon="lucide:receipt"
|
icon-color="text-orange-500"
|
icon-bg-color="bg-orange-100"
|
prefix="¥"
|
:decimals="2"
|
:value="
|
Number(fenToYuan(trendSummary?.value?.afterSaleRefundPrice || 0))
|
"
|
:percent="
|
calculateRelativeRate(
|
trendSummary?.value?.afterSaleRefundPrice,
|
trendSummary?.reference?.afterSaleRefundPrice,
|
)
|
"
|
/>
|
</Col>
|
</Row>
|
|
<!-- 折线图 -->
|
<Spin :spinning="trendLoading">
|
<EchartsUI ref="chartRef" />
|
</Spin>
|
</Card>
|
</template>
|