From 27a7e84def63644eb2bd5b476dfcfeebbe928bbb Mon Sep 17 00:00:00 2001 From: lrl <252048765@qq.com> Date: Fri, 18 Jul 2025 15:06:27 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E4=BA=A4=E6=98=93?= =?UTF-8?q?=E7=8A=B6=E5=86=B5=E7=BB=84=E4=BB=B6=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1=E6=95=B0=E6=8D=AE=E5=B1=95=E7=A4=BA=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=8E=AF=E6=AF=94=E5=A2=9E=E9=95=BF=E7=8E=87?= =?UTF-8?q?=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web-ele/src/api/mall/statistics/trade.ts | 11 +- apps/web-ele/src/views/mall/home/index.vue | 4 + .../views/mall/statistics/member/index.vue | 15 +- .../product/components/product-summary.vue | 23 +- .../components/trade-transaction-card.vue | 235 ++++++++++++++++++ .../src/views/mall/statistics/trade/index.vue | 36 ++- .../dashboard/analysis/analysis-overview.vue | 90 +++++-- .../common-ui/src/ui/dashboard/typing.ts | 2 + 8 files changed, 362 insertions(+), 54 deletions(-) create mode 100644 apps/web-ele/src/views/mall/statistics/trade/components/trade-transaction-card.vue diff --git a/apps/web-ele/src/api/mall/statistics/trade.ts b/apps/web-ele/src/api/mall/statistics/trade.ts index 151f5a4d5..0b52be510 100644 --- a/apps/web-ele/src/api/mall/statistics/trade.ts +++ b/apps/web-ele/src/api/mall/statistics/trade.ts @@ -1,6 +1,6 @@ import type { MallDataComparisonResp } from './common'; -import { formatDate, formatDate2 } from '@vben/utils'; +import { formatDate2 } from '@vben/utils'; import { requestClient } from '#/api/request'; @@ -15,7 +15,7 @@ export namespace MallTradeStatisticsApi { /** 交易状况 Request */ export interface TradeTrendReq { - times: [Date, Date]; + times: Date[]; } /** 交易状况统计 Response */ @@ -64,8 +64,11 @@ export namespace MallTradeStatisticsApi { /** 时间参数需要格式化, 确保接口能识别 */ const formatDateParam = (params: MallTradeStatisticsApi.TradeTrendReq) => { return { - times: [formatDate(params.times[0]), formatDate(params.times[1])], - } as MallTradeStatisticsApi.TradeTrendReq; + times: [ + formatDate2(params.times[0] || new Date()), + formatDate2(params.times[1] || new Date()), + ], + }; }; /** 查询交易统计 */ diff --git a/apps/web-ele/src/views/mall/home/index.vue b/apps/web-ele/src/views/mall/home/index.vue index 2a4748fea..a8c00711f 100644 --- a/apps/web-ele/src/views/mall/home/index.vue +++ b/apps/web-ele/src/views/mall/home/index.vue @@ -120,6 +120,7 @@ const loadOverview = () => { totalTitle: '昨日数据', totalValue: orderComparison.value?.reference?.orderPayPrice || 0, value: orderComparison.value?.orderPayPrice || 0, + showGrowthRate: true, }, { icon: SvgCakeIcon, @@ -127,6 +128,7 @@ const loadOverview = () => { totalTitle: '总访问量', totalValue: userComparison.value?.reference?.visitUserCount || 0, value: userComparison.value?.visitUserCount || 0, + showGrowthRate: true, }, { icon: SvgDownloadIcon, @@ -134,6 +136,7 @@ const loadOverview = () => { totalTitle: '总订单量', totalValue: orderComparison.value?.orderPayCount || 0, value: orderComparison.value?.reference?.orderPayCount || 0, + // 不显示环比增长率 }, { icon: SvgBellIcon, @@ -141,6 +144,7 @@ const loadOverview = () => { totalTitle: '总会员注册量', totalValue: userComparison.value?.registerUserCount || 0, value: userComparison.value?.reference?.registerUserCount || 0, + showGrowthRate: true, }, ]; }; diff --git a/apps/web-ele/src/views/mall/statistics/member/index.vue b/apps/web-ele/src/views/mall/statistics/member/index.vue index 2d0892522..3ef78fbfd 100644 --- a/apps/web-ele/src/views/mall/statistics/member/index.vue +++ b/apps/web-ele/src/views/mall/statistics/member/index.vue @@ -6,12 +6,7 @@ import type { MallMemberStatisticsApi } from '#/api/mall/statistics/member'; // import { onMounted, ref } from 'vue'; import { AnalysisOverview, DocAlert, Page } from '@vben/common-ui'; -import { - SvgBellIcon, - SvgCakeIcon, - SvgCardIcon, - SvgDownloadIcon, -} from '@vben/icons'; +import { SvgCakeIcon, SvgCardIcon } from '@vben/icons'; import * as MemberStatisticsApi from '#/api/mall/statistics/member'; // 会员统计数据 import MemberFunnelCard from '#/views/mall/home/components/member-funnel-card.vue'; @@ -27,22 +22,22 @@ const loadOverview = async () => { summary.value = await MemberStatisticsApi.getMemberSummary(); overviewItems.value = [ { - icon: SvgCardIcon, + icon: SvgCakeIcon, // 自定义立体用户群组图标 - 累计会员数 title: '累计会员数', value: summary.value?.userCount || 0, }, { - icon: SvgCakeIcon, + icon: SvgCardIcon, // 自定义立体信用卡图标 - 累计充值人数 title: '累计充值人数', value: summary.value?.rechargeUserCount || 0, }, { - icon: SvgDownloadIcon, + icon: SvgCardIcon, // 自定义立体钞票图标 - 累计充值金额 title: '累计充值金额', value: summary.value?.rechargePrice || 0, }, { - icon: SvgBellIcon, + icon: SvgCakeIcon, // 自定义立体用户添加图标 - 今日会员注册量 title: '今日会员注册量', value: summary.value?.expensePrice || 0, }, diff --git a/apps/web-ele/src/views/mall/statistics/product/components/product-summary.vue b/apps/web-ele/src/views/mall/statistics/product/components/product-summary.vue index 1a7717144..b17493606 100644 --- a/apps/web-ele/src/views/mall/statistics/product/components/product-summary.vue +++ b/apps/web-ele/src/views/mall/statistics/product/components/product-summary.vue @@ -8,12 +8,7 @@ import type { MallProductStatisticsApi } from '#/api/mall/statistics/product'; import { reactive, ref } from 'vue'; import { AnalysisChartCard, AnalysisOverview, confirm } from '@vben/common-ui'; -import { - SvgBellIcon, - SvgCakeIcon, - SvgDownloadIcon, - SvgEyeIcon, -} from '@vben/icons'; +import { SvgCakeIcon, SvgCardIcon, SvgEyeIcon } from '@vben/icons'; import { EchartsUI, useEcharts } from '@vben/plugins/echarts'; import { downloadFileFromBlobPart, @@ -222,10 +217,11 @@ const loadOverview = () => { icon: SvgEyeIcon, title: '商品浏览量', totalTitle: '昨日数据', - totalValue: trendSummary.value?.reference?.browseCount, + totalValue: trendSummary.value?.reference?.browseCount || 0, value: trendSummary.value?.value?.browseCount || 0, tooltip: '在选定条件下,所有商品详情页被访问的次数,一个人在统计时间内访问多次记为多次', + showGrowthRate: true, }, { icon: SvgCakeIcon, @@ -235,38 +231,43 @@ const loadOverview = () => { value: trendSummary.value?.value?.browseUserCount || 0, tooltip: '在选定条件下,访问任何商品详情页的人数,一个人在统计时间范围内访问多次只记为一个', + showGrowthRate: true, }, { - icon: SvgDownloadIcon, + icon: SvgCakeIcon, title: '支付件数', totalTitle: '昨日数据', totalValue: trendSummary.value?.reference?.orderPayCount || 0, value: trendSummary.value?.value?.orderPayCount || 0, tooltip: '在选定条件下,成功付款订单的商品件数之和', + showGrowthRate: true, }, { - icon: SvgBellIcon, + icon: SvgCardIcon, title: '支付金额', totalTitle: '昨日数据', totalValue: trendSummary.value?.reference?.afterSaleCount || 0, value: trendSummary.value?.value?.orderPayPrice || 0, tooltip: '在选定条件下,成功付款订单的商品金额之和', + showGrowthRate: true, }, { - icon: SvgBellIcon, + icon: SvgCakeIcon, title: '退款件数', totalTitle: '昨日数据', totalValue: trendSummary.value?.reference?.afterSaleCount || 0, value: trendSummary.value?.value?.afterSaleCount || 0, tooltip: '在选定条件下,成功退款的商品件数之和', + showGrowthRate: true, }, { - icon: SvgBellIcon, + icon: SvgCardIcon, title: '退款金额', totalTitle: '昨日数据', totalValue: trendSummary.value?.reference?.afterSaleRefundPrice || 0, value: trendSummary.value?.value?.afterSaleRefundPrice || 0, tooltip: '在选定条件下,成功退款的商品金额之和', + showGrowthRate: true, }, ]; }; diff --git a/apps/web-ele/src/views/mall/statistics/trade/components/trade-transaction-card.vue b/apps/web-ele/src/views/mall/statistics/trade/components/trade-transaction-card.vue new file mode 100644 index 000000000..6e1ec77dd --- /dev/null +++ b/apps/web-ele/src/views/mall/statistics/trade/components/trade-transaction-card.vue @@ -0,0 +1,235 @@ + + diff --git a/apps/web-ele/src/views/mall/statistics/trade/index.vue b/apps/web-ele/src/views/mall/statistics/trade/index.vue index bfd20d4bf..0c8d99a96 100644 --- a/apps/web-ele/src/views/mall/statistics/trade/index.vue +++ b/apps/web-ele/src/views/mall/statistics/trade/index.vue @@ -7,43 +7,48 @@ import type { MallTradeStatisticsApi } from '#/api/mall/statistics/trade'; import { onMounted, ref } from 'vue'; import { AnalysisOverview, DocAlert, Page } from '@vben/common-ui'; -import { - SvgBellIcon, - SvgCakeIcon, - SvgDownloadIcon, - SvgEyeIcon, -} from '@vben/icons'; +import { SvgCakeIcon, SvgCardIcon } from '@vben/icons'; import * as TradeStatisticsApi from '#/api/mall/statistics/trade'; +import TradeTransactionCard from './components/trade-transaction-card.vue'; + const overviewItems = ref(); const summary = ref>(); const loadOverview = () => { overviewItems.value = [ { - icon: SvgEyeIcon, + icon: SvgCakeIcon, title: '昨日订单数量', value: summary.value?.value?.yesterdayOrderCount || 0, tooltip: '昨日订单数量', + totalValue: summary?.value?.reference?.yesterdayOrderCount, + showGrowthRate: true, }, { icon: SvgCakeIcon, title: '本月订单数量', value: summary.value?.value?.monthOrderCount || 0, tooltip: '本月订单数量', + totalValue: summary?.value?.reference?.monthOrderCount, + showGrowthRate: true, }, { - icon: SvgDownloadIcon, + icon: SvgCardIcon, title: '昨日支付金额', value: summary.value?.value?.yesterdayPayPrice || 0, tooltip: '昨日支付金额', + totalValue: summary?.value?.reference?.yesterdayPayPrice, + showGrowthRate: true, }, { - icon: SvgBellIcon, + icon: SvgCardIcon, title: '本月支付金额', value: summary.value?.value?.monthPayPrice || 0, tooltip: '本月支付金额', + totalValue: summary?.value?.reference?.monthPayPrice, + showGrowthRate: true, }, ]; }; @@ -67,9 +72,14 @@ onMounted(async () => { url="https://doc.iocoder.cn/mall/statistics/" /> - +
+ +
+
+ +
diff --git a/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-overview.vue b/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-overview.vue index 9b735707f..ac76a0044 100644 --- a/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-overview.vue +++ b/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-overview.vue @@ -52,6 +52,24 @@ const gridColumnsClass = computed(() => { 'lg:grid-cols-6': colNum === 6, }; }); + +// 计算环比增长率 +const calculateGrowthRate = ( + currentValue: number, + previousValue: number, +): { isPositive: boolean; rate: number } => { + if (previousValue === 0) { + return { rate: currentValue > 0 ? 100 : 0, isPositive: currentValue >= 0 }; + } + + const rate = ((currentValue - previousValue) / previousValue) * 100; + return { rate: Math.abs(rate), isPositive: rate >= 0 }; +}; + +// 格式化增长率显示 +const formatGrowthRate = (rate: number): string => { + return rate.toFixed(1); +};