addanalysis
This commit is contained in:
@@ -130,6 +130,12 @@ export namespace AgentApi {
|
|||||||
withdraw_no?: string;
|
withdraw_no?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 提现统计数据
|
||||||
|
export interface WithdrawalStatistics {
|
||||||
|
total_withdrawal_amount: number;
|
||||||
|
today_withdrawal_amount: number;
|
||||||
|
}
|
||||||
|
|
||||||
// 代理上级抽佣相关接口
|
// 代理上级抽佣相关接口
|
||||||
export interface AgentCommissionDeductionListItem {
|
export interface AgentCommissionDeductionListItem {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -446,6 +452,15 @@ async function updateAgentMembershipConfig(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取提现统计数据
|
||||||
|
*/
|
||||||
|
async function getWithdrawalStatistics() {
|
||||||
|
return requestClient.get<AgentApi.WithdrawalStatistics>(
|
||||||
|
'/agent/agent-withdrawal/statistics',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getAgentCommissionDeductionList,
|
getAgentCommissionDeductionList,
|
||||||
getAgentCommissionList,
|
getAgentCommissionList,
|
||||||
@@ -457,6 +472,7 @@ export {
|
|||||||
getAgentRewardList,
|
getAgentRewardList,
|
||||||
getAgentWithdrawalList,
|
getAgentWithdrawalList,
|
||||||
getMembershipRechargeOrderList,
|
getMembershipRechargeOrderList,
|
||||||
|
getWithdrawalStatistics,
|
||||||
updateAgentMembershipConfig,
|
updateAgentMembershipConfig,
|
||||||
updateAgentProductionConfig,
|
updateAgentProductionConfig,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export namespace OrderApi {
|
|||||||
payment_platform: 'alipay' | 'appleiap' | 'wechat';
|
payment_platform: 'alipay' | 'appleiap' | 'wechat';
|
||||||
payment_scene: 'app' | 'h5' | 'mini_program' | 'public_account';
|
payment_scene: 'app' | 'h5' | 'mini_program' | 'public_account';
|
||||||
amount: number;
|
amount: number;
|
||||||
|
sales_cost: number;
|
||||||
status: 'closed' | 'failed' | 'paid' | 'pending' | 'refunded';
|
status: 'closed' | 'failed' | 'paid' | 'pending' | 'refunded';
|
||||||
query_state: 'cleaned' | 'failed' | 'pending' | 'processing' | 'success';
|
query_state: 'cleaned' | 'failed' | 'pending' | 'processing' | 'success';
|
||||||
create_time: string;
|
create_time: string;
|
||||||
@@ -34,6 +35,20 @@ export namespace OrderApi {
|
|||||||
refund_no: string;
|
refund_no: string;
|
||||||
amount: number;
|
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 { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||||
|
|
||||||
import { statsHistory, statsTotal } from '#/api/promotion/analytics';
|
import { getOrderList } from '#/api/order/order';
|
||||||
|
|
||||||
const chartRef = ref<EchartsUIType>();
|
const chartRef = ref<EchartsUIType>();
|
||||||
const { renderEcharts } = useEcharts(chartRef);
|
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 () => {
|
onMounted(async () => {
|
||||||
try {
|
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 dates = Array.from({ length: 30 }).map((_, index) => {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
@@ -34,47 +20,37 @@ onMounted(async () => {
|
|||||||
return date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' });
|
return date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' });
|
||||||
});
|
});
|
||||||
|
|
||||||
// 如果有历史数据,使用历史数据;否则使用模拟数据
|
// 准备日期数组,用于查询订单数据
|
||||||
let orderData = Array(30).fill(0);
|
const queryDates = Array.from({ length: 30 }).map((_, index) => {
|
||||||
if (trendData && trendData.length > 0) {
|
const date = new Date();
|
||||||
// 将历史数据按日期排序并映射到数组
|
date.setDate(date.getDate() - 29 + index);
|
||||||
const sortedData = trendData.sort((a, b) =>
|
return date.toISOString().split('T')[0]; // YYYY-MM-DD格式
|
||||||
new Date(a.stats_date).getTime() - new Date(b.stats_date).getTime()
|
});
|
||||||
);
|
|
||||||
|
|
||||||
sortedData.forEach((item) => {
|
// 获取每日订单数据
|
||||||
const itemDate = new Date(item.stats_date);
|
const orderDataPromises = queryDates.map(async (date) => {
|
||||||
const today = new Date();
|
try {
|
||||||
const daysDiff = Math.floor((today.getTime() - itemDate.getTime()) / (1000 * 60 * 60 * 24));
|
// 获取当天的开始和结束时间
|
||||||
|
const startTime = `${date} 00:00:00`;
|
||||||
|
const endTime = `${date} 23:59:59`;
|
||||||
|
|
||||||
if (daysDiff >= 0 && daysDiff < 30) {
|
// 获取当天的订单数据
|
||||||
// 使用实际日期索引
|
const response = await getOrderList({
|
||||||
orderData[29 - daysDiff] = item.pay_count || 0;
|
page: 1,
|
||||||
|
pageSize: 1,
|
||||||
|
create_time_start: startTime,
|
||||||
|
create_time_end: endTime
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.total || 0;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`获取${date}的订单数据失败:`, error);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
// 没有历史数据时,使用统计数据生成模拟数据
|
|
||||||
const todayPayCount = statsData?.today_pay_count || 0;
|
|
||||||
const totalPayCount = statsData?.total_pay_count || 0;
|
|
||||||
|
|
||||||
// 简单的线性分布模拟数据
|
// 等待所有请求完成
|
||||||
for (let i = 0; i < 30; i++) {
|
const orderData = await Promise.all(orderDataPromises);
|
||||||
// 最后一天使用今日数据,其他天按比例分布
|
|
||||||
if (i === 29) {
|
|
||||||
orderData[i] = todayPayCount;
|
|
||||||
} else {
|
|
||||||
// 按指数衰减模拟历史数据
|
|
||||||
orderData[i] = Math.max(0, Math.floor(todayPayCount * Math.exp(-0.1 * (29 - i))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 确保总和不超过总计数
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算Y轴最大值
|
// 计算Y轴最大值
|
||||||
const maxValue = Math.max(...orderData) || 10;
|
const maxValue = Math.max(...orderData) || 10;
|
||||||
@@ -89,13 +65,17 @@ onMounted(async () => {
|
|||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
barMaxWidth: 80,
|
|
||||||
data: orderData,
|
data: orderData,
|
||||||
type: 'bar',
|
type: 'line',
|
||||||
name: '订单数',
|
name: '订单数',
|
||||||
|
smooth: true,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: '#4f9cff',
|
color: '#4f9cff',
|
||||||
},
|
},
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.3,
|
||||||
|
color: '#4f9cff',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
tooltip: {
|
tooltip: {
|
||||||
@@ -113,6 +93,7 @@ onMounted(async () => {
|
|||||||
xAxis: {
|
xAxis: {
|
||||||
data: dates,
|
data: dates,
|
||||||
type: 'category',
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
max: Math.ceil(maxValue * 1.2), // 比最大值大20%作为Y轴上限
|
max: Math.ceil(maxValue * 1.2), // 比最大值大20%作为Y轴上限
|
||||||
@@ -134,13 +115,17 @@ onMounted(async () => {
|
|||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
barMaxWidth: 80,
|
|
||||||
data: Array(30).fill(0),
|
data: Array(30).fill(0),
|
||||||
type: 'bar',
|
type: 'line',
|
||||||
name: '订单数',
|
name: '订单数',
|
||||||
|
smooth: true,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: '#4f9cff',
|
color: '#4f9cff',
|
||||||
},
|
},
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.3,
|
||||||
|
color: '#4f9cff',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
tooltip: {
|
tooltip: {
|
||||||
@@ -158,6 +143,7 @@ onMounted(async () => {
|
|||||||
return date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' });
|
return date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' });
|
||||||
}),
|
}),
|
||||||
type: 'category',
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
max: 10,
|
max: 10,
|
||||||
|
|||||||
@@ -22,40 +22,55 @@ import AnalyticsVisitsSales from './analytics-visits-sales.vue';
|
|||||||
import AnalyticsVisitsSource from './analytics-visits-source.vue';
|
import AnalyticsVisitsSource from './analytics-visits-source.vue';
|
||||||
import AnalyticsVisits from './analytics-visits.vue';
|
import AnalyticsVisits from './analytics-visits.vue';
|
||||||
|
|
||||||
import { getAgentList } from '#/api/agent';
|
import { getAgentList, getWithdrawalStatistics } from '#/api/agent';
|
||||||
import { getOrderList } from '#/api/order/order';
|
import { getOrderList, getRefundStatistics, getIncomeStatistics } from '#/api/order/order';
|
||||||
import { getProductList } from '#/api/product-manage/product';
|
|
||||||
import { getPlatformUserList } from '#/api/platform-user';
|
import { getPlatformUserList } from '#/api/platform-user';
|
||||||
|
|
||||||
// 初始化概览数据
|
// 初始化概览数据
|
||||||
const overviewItems = ref<AnalysisOverviewItem[]>([
|
const overviewItems = ref<AnalysisOverviewItem[]>([
|
||||||
{
|
{
|
||||||
icon: SvgCardIcon,
|
icon: SvgCardIcon,
|
||||||
title: '平台用户数',
|
title: '总用户数',
|
||||||
totalTitle: '总用户数',
|
|
||||||
totalValue: 0,
|
|
||||||
value: 0,
|
value: 0,
|
||||||
|
todaytitle: '今日新增用户数',
|
||||||
|
todayValue: 0,
|
||||||
|
Subtitle: '总代理数',
|
||||||
|
SubValue: 0,
|
||||||
|
todaySubtitle: '今日新增代理数',
|
||||||
|
todaySubValue: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: SvgCakeIcon,
|
icon: SvgCakeIcon,
|
||||||
title: '推广访问量',
|
title: '总订单数',
|
||||||
totalTitle: '总推广访问量',
|
|
||||||
totalValue: 0,
|
|
||||||
value: 0,
|
value: 0,
|
||||||
|
todaytitle: '今日新增订单数',
|
||||||
|
todayValue: 0,
|
||||||
|
Subtitle: '代理总订单量',
|
||||||
|
SubValue: 0,
|
||||||
|
todaySubtitle: '今日新增代理订单量',
|
||||||
|
todaySubValue: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: SvgDownloadIcon,
|
icon: SvgDownloadIcon,
|
||||||
title: '产品数量',
|
title: '总收入',
|
||||||
totalTitle: '总产品数量',
|
|
||||||
totalValue: 0,
|
|
||||||
value: 0,
|
value: 0,
|
||||||
|
todaytitle: '今日新增收入',
|
||||||
|
todayValue: 0,
|
||||||
|
Subtitle: '总利润',
|
||||||
|
SubValue: 0,
|
||||||
|
todaySubtitle: '今日新增利润',
|
||||||
|
todaySubValue: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: SvgBellIcon,
|
icon: SvgBellIcon,
|
||||||
title: '代理数量',
|
title: '总提现金额',
|
||||||
totalTitle: '总代理数量',
|
|
||||||
totalValue: 0,
|
|
||||||
value: 0,
|
value: 0,
|
||||||
|
todaytitle: '今日新增提现金额',
|
||||||
|
todayValue: 0,
|
||||||
|
Subtitle: '总退款金额',
|
||||||
|
SubValue: 0,
|
||||||
|
todaySubtitle: '今日新增退款金额',
|
||||||
|
todaySubValue: 0,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -73,51 +88,128 @@ const chartTabs: TabOption[] = [
|
|||||||
// 获取统计数据
|
// 获取统计数据
|
||||||
async function fetchStatistics() {
|
async function fetchStatistics() {
|
||||||
try {
|
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 platformUserResponse = await getPlatformUserList({ page: 1, pageSize: 1 });
|
||||||
const platformUserTotal = platformUserResponse.total || 0;
|
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 orderResponse = await getOrderList({ page: 1, pageSize: 1 });
|
||||||
const orderTotal = orderResponse.total || 0;
|
const orderTotal = orderResponse.total || 0;
|
||||||
|
|
||||||
// 获取产品数据
|
// 获取代理订单数据
|
||||||
const productResponse = await getProductList({ page: 1, pageSize: 1 });
|
const agentOrderResponse = await getOrderList({ page: 1, pageSize: 1, is_agent_order: true });
|
||||||
const productTotal = productResponse.total || 0;
|
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 agentResponse = await getAgentList({ page: 1, pageSize: 1 });
|
||||||
const agentTotal = agentResponse.total || 0;
|
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 = [
|
overviewItems.value = [
|
||||||
{
|
{
|
||||||
icon: SvgCardIcon,
|
icon: SvgCardIcon,
|
||||||
title: '平台用户数',
|
title: '总用户数',
|
||||||
totalTitle: '总用户数',
|
value: platformUserTotal,
|
||||||
totalValue: platformUserTotal,
|
todaytitle: '今日新增用户数',
|
||||||
value: Math.min(100, platformUserTotal), // 显示最近的100个作为今日新增
|
todayValue: newUserCount,
|
||||||
|
Subtitle: '总代理数',
|
||||||
|
SubValue: agentTotal,
|
||||||
|
todaySubtitle: '今日新增代理数',
|
||||||
|
todaySubValue: newAgentCount,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: SvgCakeIcon,
|
icon: SvgCakeIcon,
|
||||||
title: '推广访问量',
|
title: '总订单数',
|
||||||
totalTitle: '总推广访问量',
|
value: orderTotal,
|
||||||
totalValue: orderTotal * 10, // 假设每个订单平均带来10次访问
|
todaytitle: '今日新增订单数',
|
||||||
value: Math.min(1000, orderTotal), // 显示最近的1000个作为今日新增
|
todayValue: todayOrderTotal,
|
||||||
|
Subtitle: '总代理订单量',
|
||||||
|
SubValue: agentOrderTotal,
|
||||||
|
todaySubtitle: '今日新增代理订单量',
|
||||||
|
todaySubValue: todayAgentOrderTotal,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: SvgDownloadIcon,
|
icon: SvgDownloadIcon,
|
||||||
title: '产品数量',
|
title: '总收入',
|
||||||
totalTitle: '总产品数量',
|
value: totalIncome,
|
||||||
totalValue: productTotal,
|
todaytitle: '今日新增收入',
|
||||||
value: Math.min(10, productTotal), // 显示最近的10个作为今日新增
|
todayValue: todayIncome,
|
||||||
|
Subtitle: '总利润',
|
||||||
|
SubValue: totalProfit,
|
||||||
|
todaySubtitle: '今日新增利润',
|
||||||
|
todaySubValue: todayProfit,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: SvgBellIcon,
|
icon: SvgBellIcon,
|
||||||
title: '代理数量',
|
title: '总提现金额',
|
||||||
totalTitle: '总代理数量',
|
value: totalWithdrawalAmount,
|
||||||
totalValue: agentTotal,
|
todaytitle: '今日新增提现金额',
|
||||||
value: Math.min(50, agentTotal), // 显示最近的50个作为今日新增
|
todayValue: todayWithdrawalAmount,
|
||||||
|
Subtitle: '总退款金额',
|
||||||
|
SubValue: totalRefundAmount,
|
||||||
|
todaySubtitle: '今日新增退款金额',
|
||||||
|
todaySubValue: todayRefundAmount,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -53,6 +53,14 @@ export function useColumns<T = OrderApi.Order>(
|
|||||||
return sceneMap[row.payment_scene] || row.payment_scene;
|
return sceneMap[row.payment_scene] || row.payment_scene;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: 'sales_cost',
|
||||||
|
title: '成本价',
|
||||||
|
width: 120,
|
||||||
|
formatter: ({ row }) => {
|
||||||
|
return `¥${row.sales_cost.toFixed(2)}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
field: 'amount',
|
field: 'amount',
|
||||||
title: '金额',
|
title: '金额',
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import type { AnalysisOverviewItem } from '../typing';
|
|||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
CardFooter,
|
|
||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
VbenCountToAnimator,
|
VbenCountToAnimator,
|
||||||
@@ -26,32 +25,38 @@ withDefaults(defineProps<Props>(), {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||||
<template v-for="item in items" :key="item.title">
|
<Card v-for="(item, index) in items" :key="index" class="relative overflow-hidden">
|
||||||
<Card :title="item.title" class="w-full">
|
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||||
<CardHeader>
|
<CardTitle class="text-sm font-medium">
|
||||||
<CardTitle class="text-xl">{{ item.title }}</CardTitle>
|
{{ item.title }}
|
||||||
|
</CardTitle>
|
||||||
|
<VbenIcon :icon="item.icon" class="h-4 w-4 text-muted-foreground" />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
<CardContent class="flex items-center justify-between">
|
<div class="text-2xl font-bold">
|
||||||
<VbenCountToAnimator
|
<VbenCountToAnimator :end-val="item.value" />
|
||||||
:end-val="item.value"
|
</div>
|
||||||
:start-val="1"
|
<p class="text-xs text-muted-foreground">
|
||||||
class="text-xl"
|
{{ item.todaytitle }}
|
||||||
prefix=""
|
<span class="font-medium text-foreground">
|
||||||
:decimals="item.decimals ?? 0"
|
+<VbenCountToAnimator :end-val="item.todayValue" />
|
||||||
/>
|
</span>
|
||||||
<VbenIcon :icon="item.icon" class="size-8 flex-shrink-0" />
|
</p>
|
||||||
|
<div class="mt-3 border-t pt-3">
|
||||||
|
<p class="text-xs text-muted-foreground flex justify-between">
|
||||||
|
<span>{{ item.Subtitle }}</span>
|
||||||
|
<span class="font-medium text-foreground">
|
||||||
|
<VbenCountToAnimator :end-val="item.SubValue" />
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="text-xs text-muted-foreground flex justify-between mt-1">
|
||||||
|
<span>{{ item.todaySubtitle }}</span>
|
||||||
|
<span class="font-medium text-foreground">
|
||||||
|
+<VbenCountToAnimator :end-val="item.todaySubValue" />
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter class="justify-between">
|
|
||||||
<span>{{ item.totalTitle }}</span>
|
|
||||||
<VbenCountToAnimator
|
|
||||||
:end-val="item.totalValue"
|
|
||||||
:start-val="1"
|
|
||||||
prefix=""
|
|
||||||
:decimals="item.decimals ?? 0"
|
|
||||||
/>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
</Card>
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,10 +3,13 @@ import type { Component } from 'vue';
|
|||||||
interface AnalysisOverviewItem {
|
interface AnalysisOverviewItem {
|
||||||
icon: Component | string;
|
icon: Component | string;
|
||||||
title: string;
|
title: string;
|
||||||
totalTitle: string;
|
|
||||||
totalValue: number;
|
|
||||||
value: number;
|
value: number;
|
||||||
decimals?: number;
|
todaytitle: string;
|
||||||
|
todayValue: number;
|
||||||
|
Subtitle: string;
|
||||||
|
SubValue: number;
|
||||||
|
todaySubtitle: string;
|
||||||
|
todaySubValue: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WorkbenchProjectItem {
|
interface WorkbenchProjectItem {
|
||||||
|
|||||||
Reference in New Issue
Block a user