diff --git a/apps/web-antd/src/views/mall/statistics/product/modules/summary-card.vue b/apps/web-antd/src/views/mall/statistics/product/modules/summary-card.vue index 338bcb949..dfc076368 100644 --- a/apps/web-antd/src/views/mall/statistics/product/modules/summary-card.vue +++ b/apps/web-antd/src/views/mall/statistics/product/modules/summary-card.vue @@ -219,7 +219,6 @@ async function handleExport() { " /> - 0 ? searchTimes.value : undefined, }); - // 处理数据 - // TODO @芋艿:搞到 getTradeTrendChartOptions 里; - // TODO @芋艿:貌似和 vue3 + element-plus 数据不一致; - const processedList = list.map((item) => ({ - ...item, - turnoverPrice: Number(fenToYuan(item.turnoverPrice)), - orderPayPrice: Number(fenToYuan(item.orderPayPrice)), - rechargePrice: Number(fenToYuan(item.rechargePrice)), - expensePrice: Number(fenToYuan(item.expensePrice)), - })); - // 渲染图表 - await renderEcharts(getTradeTrendChartOptions(processedList)); + await renderEcharts(getTradeTrendChartOptions(list)); } /** 导出按钮操作 */ @@ -120,8 +109,6 @@ async function handleExport() { }); // 处理下载 downloadFileFromBlobPart({ fileName: '交易状况.xlsx', source: data }); - } catch { - // 用户取消导出 } finally { exportLoading.value = false; } diff --git a/apps/web-antd/src/views/mall/statistics/trade/modules/trend-chart-options.ts b/apps/web-antd/src/views/mall/statistics/trade/modules/trend-chart-options.ts index c33d7a8f1..5c85460fb 100644 --- a/apps/web-antd/src/views/mall/statistics/trade/modules/trend-chart-options.ts +++ b/apps/web-antd/src/views/mall/statistics/trade/modules/trend-chart-options.ts @@ -1,19 +1,30 @@ import type { MallTradeStatisticsApi } from '#/api/mall/statistics/trade'; +import { fenToYuan } from '@vben/utils'; + /** 交易趋势折线图配置 */ export function getTradeTrendChartOptions( data: MallTradeStatisticsApi.TradeTrendSummary[], ): any { + // 处理数据:将分转换为元 + const processedData = data.map((item) => ({ + ...item, + turnoverPrice: Number(fenToYuan(item.turnoverPrice)), + orderPayPrice: Number(fenToYuan(item.orderPayPrice)), + rechargePrice: Number(fenToYuan(item.rechargePrice)), + expensePrice: Number(fenToYuan(item.expensePrice)), + })); + return { dataset: { dimensions: [ - 'time', + 'date', 'turnoverPrice', 'orderPayPrice', 'rechargePrice', 'expensePrice', ], - source: data, + source: processedData, }, grid: { left: 20, diff --git a/apps/web-ele/src/views/mall/product/property/data.ts b/apps/web-ele/src/views/mall/product/property/data.ts index 4f724d28d..180c15101 100644 --- a/apps/web-ele/src/views/mall/product/property/data.ts +++ b/apps/web-ele/src/views/mall/product/property/data.ts @@ -153,7 +153,7 @@ export function useValueGridFormSchema(): VbenFormSchema[] { label: '属性项', component: 'ApiSelect', componentProps: { - api: () => getPropertySimpleList(), + api: getPropertySimpleList, placeholder: '请选择属性项', labelField: 'name', valueField: 'id', diff --git a/apps/web-ele/src/views/mall/statistics/product/modules/summary-card.vue b/apps/web-ele/src/views/mall/statistics/product/modules/summary-card.vue index 6912559f8..5cdfac208 100644 --- a/apps/web-ele/src/views/mall/statistics/product/modules/summary-card.vue +++ b/apps/web-ele/src/views/mall/statistics/product/modules/summary-card.vue @@ -228,7 +228,6 @@ async function handleExport() { " /> - -import type { EchartsUIType } from '@vben/plugins/echarts'; - -import type { MallDataComparisonResp } from '#/api/mall/statistics/common'; -import type { MallTradeStatisticsApi } from '#/api/mall/statistics/trade'; -import type { AnalysisOverviewIconItem } from '#/views/mall/home/components/data'; - -import { reactive, ref } from 'vue'; - -import { confirm } from '@vben/common-ui'; -import { IconifyIcon } from '@vben/icons'; -import { EchartsUI, useEcharts } from '@vben/plugins/echarts'; -import { - calculateRelativeRate, - downloadFileFromBlobPart, - fenToYuan, - formatDate, - isSameDay, -} from '@vben/utils'; - -import dayjs from 'dayjs'; - -import * as TradeStatisticsApi from '#/api/mall/statistics/trade'; -import AnalysisChartCard from '#/views/mall/home/components/analysis-chart-card.vue'; -import AnalysisOverviewIcon from '#/views/mall/home/components/analysis-overview-icon.vue'; -import ShortcutDateRangePicker from '#/views/mall/home/components/shortcut-date-range-picker.vue'; - -const chartRef = ref(); -const { renderEcharts } = useEcharts(chartRef); -const overviewItems = ref(); -const summary = - ref>(); -const shortcutDateRangePicker = ref(); -const exportLoading = ref(false); // 导出的加载中 -const trendLoading = ref(true); // 交易状态加载中 -const loadOverview = () => { - overviewItems.value = [ - { - icon: 'fa-solid:yen-sign', - title: '营业额', - value: Number(fenToYuan(summary?.value?.value.turnoverPrice || 0)), - tooltip: '商品支付金额、充值金额', - iconColor: 'bg-blue-100', - iconBgColor: 'text-blue-500', - prefix: '¥', - decimals: 2, - percent: calculateRelativeRate( - summary?.value?.value?.turnoverPrice, - summary?.value?.reference?.turnoverPrice, - ), - }, - { - icon: 'fa-solid:shopping-cart', - title: '商品支付金额', - value: Number(fenToYuan(summary.value?.value?.orderPayPrice || 0)), - tooltip: - '用户购买商品的实际支付金额,包括微信支付、余额支付、支付宝支付、线下支付金额(拼团商品在成团之后计入,线下支付订单在后台确认支付后计入)', - iconColor: 'bg-purple-100', - iconBgColor: 'text-purple-500', - prefix: '¥', - decimals: 2, - percent: calculateRelativeRate( - summary?.value?.value?.orderPayPrice, - summary?.value?.reference?.orderPayPrice, - ), - }, - { - icon: 'fa-solid:money-check-alt', - title: '充值金额', - value: Number(fenToYuan(summary.value?.value?.rechargePrice || 0)), - tooltip: '用户成功充值的金额', - iconColor: 'bg-yellow-100', - iconBgColor: 'text-yellow-500', - prefix: '¥', - decimals: 2, - percent: calculateRelativeRate( - summary?.value?.value?.rechargePrice, - summary?.value?.reference?.rechargePrice, - ), - }, - { - icon: 'ep:warning-filled', - title: '支出金额', - value: Number(fenToYuan(summary.value?.value?.expensePrice || 0)), - tooltip: '余额支付金额、支付佣金金额、商品退款金额', - iconColor: 'bg-green-100', - iconBgColor: 'text-green-500', - prefix: '¥', - decimals: 2, - percent: calculateRelativeRate( - summary?.value?.value?.expensePrice, - summary?.value?.reference?.expensePrice, - ), - }, - { - icon: 'fa-solid:wallet', - title: '余额支付金额', - value: Number(fenToYuan(summary.value?.value?.walletPayPrice || 0)), - tooltip: '余额支付金额、支付佣金金额、商品退款金额', - iconColor: 'bg-cyan-100', - iconBgColor: 'text-cyan-500', - prefix: '¥', - decimals: 2, - percent: calculateRelativeRate( - summary?.value?.value?.walletPayPrice, - summary?.value?.reference?.walletPayPrice, - ), - }, - { - icon: 'fa-solid:award', - title: '支付佣金金额', - value: Number( - fenToYuan(summary.value?.value?.brokerageSettlementPrice || 0), - ), - tooltip: '后台给推广员支付的推广佣金,以实际支付为准', - iconColor: 'bg-yellow-100', - iconBgColor: 'text-yellow-500', - prefix: '¥', - decimals: 2, - percent: calculateRelativeRate( - summary?.value?.value?.brokerageSettlementPrice, - summary?.value?.reference?.brokerageSettlementPrice, - ), - }, - { - icon: 'fa-solid:times-circle', - title: '商品退款金额', - value: Number(fenToYuan(summary.value?.value?.afterSaleRefundPrice || 0)), - tooltip: '用户成功退款的商品金额', - iconColor: 'bg-blue-100', - iconBgColor: 'text-blue-500', - prefix: '¥', - decimals: 2, - percent: calculateRelativeRate( - summary?.value?.value?.afterSaleRefundPrice, - summary?.value?.reference?.afterSaleRefundPrice, - ), - }, - ]; -}; - -/** 导出按钮操作 */ -const handleExport = async () => { - try { - // 导出的二次确认 - await confirm('确定要导出交易状况吗?'); - // 发起导出 - exportLoading.value = true; - const times = shortcutDateRangePicker.value.times; - const data = await TradeStatisticsApi.exportTradeStatisticsExcel({ times }); - downloadFileFromBlobPart({ fileName: '交易状况.xls', source: data }); - } finally { - exportLoading.value = false; - } -}; - -const getTradeTrendData = async () => { - trendLoading.value = true; - // 1. 处理时间: 开始与截止在同一天的, 折线图出不来, 需要延长一天 - const times = shortcutDateRangePicker.value.times; - if (isSameDay(times[0], times[1])) { - // 前天 - times[0] = formatDate(dayjs(times[0]).subtract(1, 'd').toDate()); - } - // 查询数据 - await Promise.all([getTradeStatisticsAnalyse(), getTradeStatisticsList()]); - trendLoading.value = false; - - loadOverview(); - renderEcharts(lineChartOptions as any); -}; - -/** 查询交易状况数据统计 */ -const getTradeStatisticsAnalyse = async () => { - const times = shortcutDateRangePicker.value.times; - summary.value = await TradeStatisticsApi.getTradeStatisticsAnalyse({ - times, - }); -}; - -/** 查询交易状况数据列表 */ -const getTradeStatisticsList = async () => { - // 查询数据 - const times = shortcutDateRangePicker.value.times; - const list = await TradeStatisticsApi.getTradeStatisticsList({ times }); - // 处理数据 - for (const item of list) { - item.turnoverPrice = Number(fenToYuan(item.turnoverPrice)); - item.orderPayPrice = Number(fenToYuan(item.orderPayPrice)); - item.rechargePrice = Number(fenToYuan(item.rechargePrice)); - item.expensePrice = Number(fenToYuan(item.expensePrice)); - } - // 更新 Echarts 数据 - if (lineChartOptions.dataset && lineChartOptions.dataset.source) { - lineChartOptions.dataset.source = list; - } -}; - -/** 折线图配置 */ -const lineChartOptions = reactive({ - dataset: { - dimensions: [ - 'date', - 'turnoverPrice', - 'orderPayPrice', - 'rechargePrice', - 'expensePrice', - ], - source: [] as MallTradeStatisticsApi.TradeTrendSummary[], - }, - grid: { - left: 20, - right: 20, - bottom: 20, - top: 80, - containLabel: true, - }, - legend: { - top: 50, - }, - series: [ - { name: '营业额', type: 'line', smooth: true }, - { name: '商品支付金额', type: 'line', smooth: true }, - { name: '充值金额', type: 'line', smooth: true }, - { name: '支出金额', type: 'line', smooth: true }, - ], - toolbox: { - feature: { - // 数据区域缩放 - dataZoom: { - yAxisIndex: false, // Y轴不缩放 - }, - brush: { - type: ['lineX', 'clear'] as const, // 区域缩放按钮、还原按钮 - }, - saveAsImage: { show: true, name: '交易状况' }, // 保存为图片 - }, - }, - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'cross', - }, - padding: [5, 10], - }, - xAxis: { - type: 'category' as const, - boundaryGap: false, - axisTick: { - show: false, - }, - }, - yAxis: { - axisTick: { - show: false, - }, - }, -}); - - 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 4aae9367d..153de4d0c 100644 --- a/apps/web-ele/src/views/mall/statistics/trade/index.vue +++ b/apps/web-ele/src/views/mall/statistics/trade/index.vue @@ -1,93 +1,120 @@ diff --git a/apps/web-ele/src/views/mall/statistics/trade/modules/trend-card.vue b/apps/web-ele/src/views/mall/statistics/trade/modules/trend-card.vue new file mode 100644 index 000000000..1c6090858 --- /dev/null +++ b/apps/web-ele/src/views/mall/statistics/trade/modules/trend-card.vue @@ -0,0 +1,282 @@ + + + + diff --git a/apps/web-ele/src/views/mall/statistics/trade/modules/trend-chart-options.ts b/apps/web-ele/src/views/mall/statistics/trade/modules/trend-chart-options.ts new file mode 100644 index 000000000..5c85460fb --- /dev/null +++ b/apps/web-ele/src/views/mall/statistics/trade/modules/trend-chart-options.ts @@ -0,0 +1,124 @@ +import type { MallTradeStatisticsApi } from '#/api/mall/statistics/trade'; + +import { fenToYuan } from '@vben/utils'; + +/** 交易趋势折线图配置 */ +export function getTradeTrendChartOptions( + data: MallTradeStatisticsApi.TradeTrendSummary[], +): any { + // 处理数据:将分转换为元 + const processedData = data.map((item) => ({ + ...item, + turnoverPrice: Number(fenToYuan(item.turnoverPrice)), + orderPayPrice: Number(fenToYuan(item.orderPayPrice)), + rechargePrice: Number(fenToYuan(item.rechargePrice)), + expensePrice: Number(fenToYuan(item.expensePrice)), + })); + + return { + dataset: { + dimensions: [ + 'date', + 'turnoverPrice', + 'orderPayPrice', + 'rechargePrice', + 'expensePrice', + ], + source: processedData, + }, + grid: { + left: 20, + right: 20, + bottom: 20, + top: 80, + containLabel: true, + }, + legend: { + top: 50, + }, + series: [ + { + name: '营业额', + type: 'line', + smooth: true, + itemStyle: { color: '#1890FF' }, + }, + { + name: '商品支付金额', + type: 'line', + smooth: true, + itemStyle: { color: '#722ED1' }, + }, + { + name: '充值金额', + type: 'line', + smooth: true, + itemStyle: { color: '#FAAD14' }, + }, + { + name: '支出金额', + type: 'line', + smooth: true, + itemStyle: { color: '#52C41A' }, + }, + ], + toolbox: { + feature: { + // 数据区域缩放 + dataZoom: { + yAxisIndex: false, // Y轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { + show: true, + name: '交易状况', + }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'cross', + }, + padding: [5, 10], + formatter(params: any) { + let result = `
${params[0].data.time}
`; + params.forEach((item: any) => { + result += `
+ + ${item.seriesName}: ¥${item.data[item.dimensionNames[item.encode.y[0]]]} +
`; + }); + return result; + }, + }, + xAxis: { + type: 'category', + boundaryGap: false, + axisTick: { + show: false, + }, + }, + yAxis: { + type: 'value', + axisLine: { + show: false, + }, + axisTick: { + show: false, + }, + axisLabel: { + formatter: '¥{value}', + color: '#7F8B9C', + }, + splitLine: { + show: true, + lineStyle: { + color: '#F5F7F9', + }, + }, + }, + }; +}