addanalysis
This commit is contained in:
@@ -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<AgentApi.WithdrawalStatistics>(
|
||||
'/agent/agent-withdrawal/statistics',
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
getAgentCommissionDeductionList,
|
||||
getAgentCommissionList,
|
||||
@@ -457,6 +472,7 @@ export {
|
||||
getAgentRewardList,
|
||||
getAgentWithdrawalList,
|
||||
getMembershipRechargeOrderList,
|
||||
getWithdrawalStatistics,
|
||||
updateAgentMembershipConfig,
|
||||
updateAgentProductionConfig,
|
||||
};
|
||||
|
||||
@@ -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<OrderApi.RefundStatistics>('/order/refund-statistics');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取收入统计数据
|
||||
*/
|
||||
async function getIncomeStatistics() {
|
||||
return requestClient.get<OrderApi.IncomeStatistics>('/order/revenue-statistics');
|
||||
}
|
||||
|
||||
export { getOrderList, refundOrder, getRefundStatistics, getIncomeStatistics };
|
||||
|
||||
@@ -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<EchartsUIType>();
|
||||
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,
|
||||
|
||||
@@ -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<AnalysisOverviewItem[]>([
|
||||
{
|
||||
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) {
|
||||
|
||||
@@ -53,6 +53,14 @@ export function useColumns<T = OrderApi.Order>(
|
||||
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: '金额',
|
||||
|
||||
Reference in New Issue
Block a user