From b392f9cf0d3f01f568ea6ed8b043fc113fe370c7 Mon Sep 17 00:00:00 2001 From: 18278715334 <18278715334@163.com> Date: Sat, 27 Dec 2025 13:51:08 +0800 Subject: [PATCH] addanalysis --- apps/web-antd/src/api/agent/agent.ts | 16 ++ apps/web-antd/src/api/order/order.ts | 31 +++- .../dashboard/analytics/analytics-visits.vue | 102 +++++------ .../src/views/dashboard/analytics/index.vue | 164 ++++++++++++++---- apps/web-antd/src/views/order/order/data.ts | 8 + .../dashboard/analysis/analysis-overview.vue | 61 ++++--- .../common-ui/src/ui/dashboard/typing.ts | 9 +- 7 files changed, 265 insertions(+), 126 deletions(-) diff --git a/apps/web-antd/src/api/agent/agent.ts b/apps/web-antd/src/api/agent/agent.ts index 95aed33..4ca04ff 100644 --- a/apps/web-antd/src/api/agent/agent.ts +++ b/apps/web-antd/src/api/agent/agent.ts @@ -130,6 +130,12 @@ export namespace AgentApi { withdraw_no?: string; } + // 提现统计数据 + export interface WithdrawalStatistics { + total_withdrawal_amount: number; + today_withdrawal_amount: number; + } + // 代理上级抽佣相关接口 export interface AgentCommissionDeductionListItem { id: number; @@ -446,6 +452,15 @@ async function updateAgentMembershipConfig( ); } +/** + * 获取提现统计数据 + */ +async function getWithdrawalStatistics() { + return requestClient.get( + '/agent/agent-withdrawal/statistics', + ); +} + export { getAgentCommissionDeductionList, getAgentCommissionList, @@ -457,6 +472,7 @@ export { getAgentRewardList, getAgentWithdrawalList, getMembershipRechargeOrderList, + getWithdrawalStatistics, updateAgentMembershipConfig, updateAgentProductionConfig, }; diff --git a/apps/web-antd/src/api/order/order.ts b/apps/web-antd/src/api/order/order.ts index 22a7310..1457626 100644 --- a/apps/web-antd/src/api/order/order.ts +++ b/apps/web-antd/src/api/order/order.ts @@ -11,6 +11,7 @@ export namespace OrderApi { payment_platform: 'alipay' | 'appleiap' | 'wechat'; payment_scene: 'app' | 'h5' | 'mini_program' | 'public_account'; amount: number; + sales_cost: number; status: 'closed' | 'failed' | 'paid' | 'pending' | 'refunded'; query_state: 'cleaned' | 'failed' | 'pending' | 'processing' | 'success'; create_time: string; @@ -34,6 +35,20 @@ export namespace OrderApi { refund_no: string; amount: number; } + + // 退款统计数据 + export interface RefundStatistics { + total_refund_amount: number; + today_refund_amount: number; + } + + // 收入统计数据 + export interface IncomeStatistics { + total_revenue_amount: number; + today_revenue_amount: number; + total_profit_amount: number; + today_profit_amount: number; + } } /** @@ -57,4 +72,18 @@ async function refundOrder(id: number, data: OrderApi.RefundOrderRequest) { ); } -export { getOrderList, refundOrder }; +/** + * 获取退款统计数据 + */ +async function getRefundStatistics() { + return requestClient.get('/order/refund-statistics'); +} + +/** + * 获取收入统计数据 + */ +async function getIncomeStatistics() { + return requestClient.get('/order/revenue-statistics'); +} + +export { getOrderList, refundOrder, getRefundStatistics, getIncomeStatistics }; diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-visits.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-visits.vue index 8fc2d71..6c90219 100644 --- a/apps/web-antd/src/views/dashboard/analytics/analytics-visits.vue +++ b/apps/web-antd/src/views/dashboard/analytics/analytics-visits.vue @@ -5,28 +5,14 @@ import { onMounted, ref } from 'vue'; import { EchartsUI, useEcharts } from '@vben/plugins/echarts'; -import { statsHistory, statsTotal } from '#/api/promotion/analytics'; +import { getOrderList } from '#/api/order/order'; const chartRef = ref(); const { renderEcharts } = useEcharts(chartRef); -// 获取30天前的日期 -const getDateString = (daysAgo: number) => { - const date = new Date(); - date.setDate(date.getDate() - daysAgo); - return date.toISOString().split('T')[0]; -}; onMounted(async () => { try { - // 获取趋势数据 - const endDate = getDateString(0); // 今天 - const startDate = getDateString(29); // 29天前 - const trendData = await statsHistory({ start_date: startDate, end_date: endDate }); - - // 获取统计数据 - const statsData = await statsTotal(); - // 准备图表数据 const dates = Array.from({ length: 30 }).map((_, index) => { const date = new Date(); @@ -34,47 +20,37 @@ onMounted(async () => { return date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' }); }); - // 如果有历史数据,使用历史数据;否则使用模拟数据 - let orderData = Array(30).fill(0); - if (trendData && trendData.length > 0) { - // 将历史数据按日期排序并映射到数组 - const sortedData = trendData.sort((a, b) => - new Date(a.stats_date).getTime() - new Date(b.stats_date).getTime() - ); - - sortedData.forEach((item) => { - const itemDate = new Date(item.stats_date); - const today = new Date(); - const daysDiff = Math.floor((today.getTime() - itemDate.getTime()) / (1000 * 60 * 60 * 24)); + // 准备日期数组,用于查询订单数据 + const queryDates = Array.from({ length: 30 }).map((_, index) => { + const date = new Date(); + date.setDate(date.getDate() - 29 + index); + return date.toISOString().split('T')[0]; // YYYY-MM-DD格式 + }); + + // 获取每日订单数据 + const orderDataPromises = queryDates.map(async (date) => { + try { + // 获取当天的开始和结束时间 + const startTime = `${date} 00:00:00`; + const endTime = `${date} 23:59:59`; - if (daysDiff >= 0 && daysDiff < 30) { - // 使用实际日期索引 - orderData[29 - daysDiff] = item.pay_count || 0; - } - }); - } else { - // 没有历史数据时,使用统计数据生成模拟数据 - const todayPayCount = statsData?.today_pay_count || 0; - const totalPayCount = statsData?.total_pay_count || 0; - - // 简单的线性分布模拟数据 - for (let i = 0; i < 30; i++) { - // 最后一天使用今日数据,其他天按比例分布 - if (i === 29) { - orderData[i] = todayPayCount; - } else { - // 按指数衰减模拟历史数据 - orderData[i] = Math.max(0, Math.floor(todayPayCount * Math.exp(-0.1 * (29 - i)))); - } + // 获取当天的订单数据 + const response = await getOrderList({ + page: 1, + pageSize: 1, + create_time_start: startTime, + create_time_end: endTime + }); + + return response.total || 0; + } catch (error) { + console.error(`获取${date}的订单数据失败:`, error); + return 0; } - - // 确保总和不超过总计数 - const sum = orderData.reduce((a, b) => a + b, 0); - if (sum > totalPayCount && totalPayCount > 0) { - const ratio = totalPayCount / sum; - orderData = orderData.map(val => Math.floor(val * ratio)); - } - } + }); + + // 等待所有请求完成 + const orderData = await Promise.all(orderDataPromises); // 计算Y轴最大值 const maxValue = Math.max(...orderData) || 10; @@ -89,13 +65,17 @@ onMounted(async () => { }, series: [ { - barMaxWidth: 80, data: orderData, - type: 'bar', + type: 'line', name: '订单数', + smooth: true, itemStyle: { color: '#4f9cff', }, + areaStyle: { + opacity: 0.3, + color: '#4f9cff', + }, }, ], tooltip: { @@ -113,6 +93,7 @@ onMounted(async () => { xAxis: { data: dates, type: 'category', + boundaryGap: false, }, yAxis: { max: Math.ceil(maxValue * 1.2), // 比最大值大20%作为Y轴上限 @@ -134,13 +115,17 @@ onMounted(async () => { }, series: [ { - barMaxWidth: 80, data: Array(30).fill(0), - type: 'bar', + type: 'line', name: '订单数', + smooth: true, itemStyle: { color: '#4f9cff', }, + areaStyle: { + opacity: 0.3, + color: '#4f9cff', + }, }, ], tooltip: { @@ -158,6 +143,7 @@ onMounted(async () => { return date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' }); }), type: 'category', + boundaryGap: false, }, yAxis: { max: 10, diff --git a/apps/web-antd/src/views/dashboard/analytics/index.vue b/apps/web-antd/src/views/dashboard/analytics/index.vue index 483bcb0..eae9861 100644 --- a/apps/web-antd/src/views/dashboard/analytics/index.vue +++ b/apps/web-antd/src/views/dashboard/analytics/index.vue @@ -22,40 +22,55 @@ import AnalyticsVisitsSales from './analytics-visits-sales.vue'; import AnalyticsVisitsSource from './analytics-visits-source.vue'; import AnalyticsVisits from './analytics-visits.vue'; -import { getAgentList } from '#/api/agent'; -import { getOrderList } from '#/api/order/order'; -import { getProductList } from '#/api/product-manage/product'; +import { getAgentList, getWithdrawalStatistics } from '#/api/agent'; +import { getOrderList, getRefundStatistics, getIncomeStatistics } from '#/api/order/order'; import { getPlatformUserList } from '#/api/platform-user'; // 初始化概览数据 const overviewItems = ref([ { icon: SvgCardIcon, - title: '平台用户数', - totalTitle: '总用户数', - totalValue: 0, + title: '总用户数', value: 0, + todaytitle: '今日新增用户数', + todayValue: 0, + Subtitle: '总代理数', + SubValue: 0, + todaySubtitle: '今日新增代理数', + todaySubValue: 0, }, { icon: SvgCakeIcon, - title: '推广访问量', - totalTitle: '总推广访问量', - totalValue: 0, + title: '总订单数', value: 0, + todaytitle: '今日新增订单数', + todayValue: 0, + Subtitle: '代理总订单量', + SubValue: 0, + todaySubtitle: '今日新增代理订单量', + todaySubValue: 0, }, { icon: SvgDownloadIcon, - title: '产品数量', - totalTitle: '总产品数量', - totalValue: 0, + title: '总收入', value: 0, + todaytitle: '今日新增收入', + todayValue: 0, + Subtitle: '总利润', + SubValue: 0, + todaySubtitle: '今日新增利润', + todaySubValue: 0, }, { icon: SvgBellIcon, - title: '代理数量', - totalTitle: '总代理数量', - totalValue: 0, + title: '总提现金额', value: 0, + todaytitle: '今日新增提现金额', + todayValue: 0, + Subtitle: '总退款金额', + SubValue: 0, + todaySubtitle: '今日新增退款金额', + todaySubValue: 0, }, ]); @@ -73,51 +88,128 @@ const chartTabs: TabOption[] = [ // 获取统计数据 async function fetchStatistics() { try { - // 获取平台用户数据 + // 获取今日的开始和结束时间 + const today = new Date(); + // 将时间格式化为后端期望的格式 (YYYY-MM-DD HH:MM:SS) + const startTime = new Date(today.getFullYear(), today.getMonth(), today.getDate()).toISOString().replace('T', ' ').substring(0, 19); + const endTime = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1).toISOString().replace('T', ' ').substring(0, 19); + + // 获取平台用户数据(总数) const platformUserResponse = await getPlatformUserList({ page: 1, pageSize: 1 }); const platformUserTotal = platformUserResponse.total || 0; + // 获取今日新增用户数 + // 由于平台用户API不支持时间过滤,我们需要获取更多数据并在前端过滤 + const newUserResponse = await getPlatformUserList({ page: 1, pageSize: 1000 }); + const newUserCount = newUserResponse.items?.filter(user => { + const userCreateTime = new Date(user.create_time); + return userCreateTime >= new Date(startTime) && userCreateTime < new Date(endTime); + }).length || 0; + // 获取订单数据 const orderResponse = await getOrderList({ page: 1, pageSize: 1 }); const orderTotal = orderResponse.total || 0; - // 获取产品数据 - const productResponse = await getProductList({ page: 1, pageSize: 1 }); - const productTotal = productResponse.total || 0; + // 获取代理订单数据 + const agentOrderResponse = await getOrderList({ page: 1, pageSize: 1, is_agent_order: true }); + const agentOrderTotal = agentOrderResponse.total || 0; - // 获取代理数据 + // 获取今日新增订单数 + const todayOrderResponse = await getOrderList({ + page: 1, + pageSize: 1000, + create_time_start: startTime, + create_time_end: endTime + }); + const todayOrderTotal = todayOrderResponse.total || 0; + + // 获取今日新增代理订单数 + const todayAgentOrderResponse = await getOrderList({ + page: 1, + pageSize: 1000, + is_agent_order: true, + create_time_start: startTime, + create_time_end: endTime + }); + const todayAgentOrderTotal = todayAgentOrderResponse.total || 0; + + // Product data is no longer needed for order statistics + + // 获取代理数据(总数) const agentResponse = await getAgentList({ page: 1, pageSize: 1 }); const agentTotal = agentResponse.total || 0; + // 获取今日新增代理数 + const newAgentResponse = await getAgentList({ + page: 1, + pageSize: 100, + create_time_start: startTime, + create_time_end: endTime + }); + const newAgentCount = newAgentResponse.total || 0; + + // 获取提现统计数据 + const withdrawalStatsResponse = await getWithdrawalStatistics(); + const totalWithdrawalAmount = withdrawalStatsResponse.total_withdrawal_amount || 0; + const todayWithdrawalAmount = withdrawalStatsResponse.today_withdrawal_amount || 0; + + // 获取退款统计数据 + const refundStatsResponse = await getRefundStatistics(); + const totalRefundAmount = refundStatsResponse.total_refund_amount || 0; + const todayRefundAmount = refundStatsResponse.today_refund_amount || 0; + + // 获取收入统计数据 + const incomeStatsResponse = await getIncomeStatistics(); + const totalIncome = incomeStatsResponse.total_revenue_amount || 0; + const todayIncome = incomeStatsResponse.today_revenue_amount || 0; + const totalProfit = incomeStatsResponse.total_profit_amount || 0; + const todayProfit = incomeStatsResponse.today_profit_amount || 0; + // 更新概览数据 overviewItems.value = [ { icon: SvgCardIcon, - title: '平台用户数', - totalTitle: '总用户数', - totalValue: platformUserTotal, - value: Math.min(100, platformUserTotal), // 显示最近的100个作为今日新增 + title: '总用户数', + value: platformUserTotal, + todaytitle: '今日新增用户数', + todayValue: newUserCount, + Subtitle: '总代理数', + SubValue: agentTotal, + todaySubtitle: '今日新增代理数', + todaySubValue: newAgentCount, }, { icon: SvgCakeIcon, - title: '推广访问量', - totalTitle: '总推广访问量', - totalValue: orderTotal * 10, // 假设每个订单平均带来10次访问 - value: Math.min(1000, orderTotal), // 显示最近的1000个作为今日新增 + title: '总订单数', + value: orderTotal, + todaytitle: '今日新增订单数', + todayValue: todayOrderTotal, + Subtitle: '总代理订单量', + SubValue: agentOrderTotal, + todaySubtitle: '今日新增代理订单量', + todaySubValue: todayAgentOrderTotal, }, { icon: SvgDownloadIcon, - title: '产品数量', - totalTitle: '总产品数量', - totalValue: productTotal, - value: Math.min(10, productTotal), // 显示最近的10个作为今日新增 + title: '总收入', + value: totalIncome, + todaytitle: '今日新增收入', + todayValue: todayIncome, + Subtitle: '总利润', + SubValue: totalProfit, + todaySubtitle: '今日新增利润', + todaySubValue: todayProfit, }, { icon: SvgBellIcon, - title: '代理数量', - totalTitle: '总代理数量', - totalValue: agentTotal, - value: Math.min(50, agentTotal), // 显示最近的50个作为今日新增 + title: '总提现金额', + value: totalWithdrawalAmount, + todaytitle: '今日新增提现金额', + todayValue: todayWithdrawalAmount, + Subtitle: '总退款金额', + SubValue: totalRefundAmount, + todaySubtitle: '今日新增退款金额', + todaySubValue: todayRefundAmount, }, ]; } catch (error) { diff --git a/apps/web-antd/src/views/order/order/data.ts b/apps/web-antd/src/views/order/order/data.ts index 5405d01..05aea8c 100644 --- a/apps/web-antd/src/views/order/order/data.ts +++ b/apps/web-antd/src/views/order/order/data.ts @@ -53,6 +53,14 @@ export function useColumns( return sceneMap[row.payment_scene] || row.payment_scene; }, }, + { + field: 'sales_cost', + title: '成本价', + width: 120, + formatter: ({ row }) => { + return `¥${row.sales_cost.toFixed(2)}`; + }, + }, { field: 'amount', title: '金额', 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 60076ca..8878a0f 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 @@ -4,7 +4,6 @@ import type { AnalysisOverviewItem } from '../typing'; import { Card, CardContent, - CardFooter, CardHeader, CardTitle, VbenCountToAnimator, @@ -26,32 +25,38 @@ withDefaults(defineProps(), { diff --git a/packages/effects/common-ui/src/ui/dashboard/typing.ts b/packages/effects/common-ui/src/ui/dashboard/typing.ts index 709cd8a..6bcd865 100644 --- a/packages/effects/common-ui/src/ui/dashboard/typing.ts +++ b/packages/effects/common-ui/src/ui/dashboard/typing.ts @@ -3,10 +3,13 @@ import type { Component } from 'vue'; interface AnalysisOverviewItem { icon: Component | string; title: string; - totalTitle: string; - totalValue: number; value: number; - decimals?: number; + todaytitle: string; + todayValue: number; + Subtitle: string; + SubValue: number; + todaySubtitle: string; + todaySubValue: number; } interface WorkbenchProjectItem {