fix and add
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Deploy Website on push / Rerun on failure (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled

This commit is contained in:
2025-12-29 16:13:31 +08:00
parent 48a102b0a5
commit 00523bae4c
8 changed files with 353 additions and 132 deletions

View File

@@ -134,14 +134,14 @@ export namespace AgentApi {
agent_id?: number;
status?: number;
withdraw_no?: string;
status?: number;
withdraw_no?: string;
}
// 提现统计数据
export interface WithdrawalStatistics {
total_withdrawal_amount: number;
today_withdrawal_amount: number;
total_actual_amount: number;
total_tax_amount: number;
}
// 代理订单统计数据
@@ -156,6 +156,20 @@ export interface AgentStatistics {
today_agent_count: number; // 今日新增代理数
}
// 代理链接产品统计项
export interface AgentLinkProductStatisticsItem {
product_name: string;
link_count: number;
}
// 代理链接产品统计响应
export interface AgentLinkProductStatisticsResp {
items: AgentLinkProductStatisticsItem[];
}
// 代理链接产品统计请求参数
export interface GetAgentLinkProductStatisticsParams {}
// 代理上级抽佣相关接口
export interface AgentCommissionDeductionListItem {
id: number;
@@ -517,10 +531,20 @@ async function getAgentStatistics() {
);
}
/**
* 获取代理链接产品统计数据
*/
async function getAgentLinkProductStatistics() {
return requestClient.get<AgentApi.AgentLinkProductStatisticsResp>(
'/agent/agent-link/product-statistics',
);
}
export {
getAgentCommissionDeductionList,
getAgentCommissionList,
getAgentLinkList,
getAgentLinkProductStatistics,
getAgentList,
getAgentMembershipConfigList,
getAgentOrderStatistics,

View File

@@ -0,0 +1,28 @@
import { requestClient } from '#/api/request';
export namespace OrderStatisticsApi {
// 订单统计数据项
export interface OrderStatisticsItem {
date: string; // 日期
count: number; // 订单数量
amount: number; // 订单金额
}
// 订单统计响应
export interface OrderStatisticsResponse {
items: OrderStatisticsItem[];
}
// 时间维度类型
export type TimeDimension = 'day' | 'month' | 'year' | 'all';
}
/**
* 获取订单统计数据
* @param dimension 时间维度day-日(当月1号到今天)month-月(今年1月到当月)year-年(过去5年)all-全部(按日统计)
*/
export function getOrderStatistics(dimension: OrderStatisticsApi.TimeDimension) {
return requestClient.get<OrderStatisticsApi.OrderStatisticsResponse>('/order/statistics', {
params: { dimension }
});
}

View File

@@ -49,6 +49,12 @@ export namespace OrderApi {
total_profit_amount: number;
today_profit_amount: number;
}
// 订单来源统计数据
export interface OrderSourceStatistics {
product_name: string;
order_count: number;
}
}
/**
@@ -86,4 +92,11 @@ async function getIncomeStatistics() {
return requestClient.get<OrderApi.IncomeStatistics>('/order/revenue-statistics');
}
export { getOrderList, refundOrder, getRefundStatistics, getIncomeStatistics };
/**
* 获取订单来源统计数据
*/
async function getOrderSourceStatistics() {
return requestClient.get<{ items: OrderApi.OrderSourceStatistics[] }>('/order/source-statistics');
}
export { getOrderList, refundOrder, getRefundStatistics, getIncomeStatistics, getOrderSourceStatistics };

View File

@@ -4,63 +4,91 @@ import type { EchartsUIType } from '@vben/plugins/echarts';
import { onMounted, ref } from 'vue';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
import { getOrderSourceStatistics } from '#/api/order/order';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);
const loading = ref(false);
// 获取订单来源统计数据
async function fetchOrderSourceStatistics() {
try {
loading.value = true;
const response = await getOrderSourceStatistics();
// 提取产品名称和订单数量
const data = response.items.map(item => ({
name: item.product_name,
value: item.order_count,
}));
// 如果有数据,则渲染图表
if (data && data.length > 0) {
renderEcharts({
legend: {
bottom: '2%',
left: 'center',
data: data.map(item => item.name),
},
series: [
{
animationDelay() {
return Math.random() * 100;
},
animationEasing: 'exponentialInOut',
animationType: 'scale',
avoidLabelOverlap: false,
color: [
'#5ab1ef', '#b6a2de', '#67e0e3', '#2ec7c9',
'#ffb980', '#d87a80', '#8d98b3', '#e5cf0d',
'#97b552', '#95706d', '#dc69aa', '#07a2a4',
'#9a7fd1', '#588dd5', '#f5994e', '#c05050'
],
data: data,
emphasis: {
label: {
fontSize: '12',
fontWeight: 'bold',
show: true,
},
},
itemStyle: {
borderRadius: 10,
borderWidth: 2,
},
label: {
position: 'center',
show: false,
},
labelLine: {
show: false,
},
name: '订单来源',
radius: ['40%', '65%'],
type: 'pie',
},
],
tooltip: {
trigger: 'item',
formatter: '{b}: {c} ({d}%)',
},
});
}
} catch (error) {
console.error('获取订单来源统计数据失败:', error);
} finally {
loading.value = false;
}
}
onMounted(() => {
renderEcharts({
legend: {
bottom: '2%',
left: 'center',
data: ['产品A', '产品B', '产品C', '产品D'],
},
series: [
{
animationDelay() {
return Math.random() * 100;
},
animationEasing: 'exponentialInOut',
animationType: 'scale',
avoidLabelOverlap: false,
color: ['#5ab1ef', '#b6a2de', '#67e0e3', '#2ec7c9'],
data: [
{ name: '产品A', value: 1048 },
{ name: '产品B', value: 735 },
{ name: '产品C', value: 580 },
{ name: '产品D', value: 484 },
],
emphasis: {
label: {
fontSize: '12',
fontWeight: 'bold',
show: true,
},
},
itemStyle: {
// borderColor: '#fff',
borderRadius: 10,
borderWidth: 2,
},
label: {
position: 'center',
show: false,
},
labelLine: {
show: false,
},
name: '订单来源',
radius: ['40%', '65%'],
type: 'pie',
},
],
tooltip: {
trigger: 'item',
},
});
fetchOrderSourceStatistics();
});
</script>
<template>
<EchartsUI ref="chartRef" />
<div v-if="loading" class="flex justify-center items-center h-64">
加载中...
</div>
<EchartsUI v-else ref="chartRef" />
</template>

View File

@@ -1,59 +1,59 @@
<script lang="ts" setup>
import type { EchartsUIType } from '@vben/plugins/echarts';
import { onMounted, ref } from 'vue';
import { onMounted, ref, watch } from 'vue';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
import { getOrderList } from '#/api/order/order';
import { Button } from 'ant-design-vue';
import { getOrderStatistics } from '#/api/order/order-statistics';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);
// 时间维度状态
const timeDimension = ref<'day' | 'month' | 'year' | 'all'>('day');
onMounted(async () => {
// 获取订单统计数据
async function fetchOrderStatistics() {
try {
// 准备图表数据
const dates = Array.from({ length: 30 }).map((_, index) => {
const date = new Date();
date.setDate(date.getDate() - 29 + index);
return date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' });
});
console.log('Fetching order statistics with dimension:', timeDimension.value);
// 准备日期数组,用于查询订单数据
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格式
});
// 使用后端API获取数据
const response = await getOrderStatistics(timeDimension.value);
console.log('Order statistics response:', response);
// 获取每日订单数据
const orderDataPromises = queryDates.map(async (date) => {
try {
// 获取当天的开始和结束时间
const startTime = `${date} 00:00:00`;
const endTime = `${date} 23:59:59`;
// 获取当天的订单数据
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;
let items = response.items || [];
// 如果后端返回空数据,显示空状态
if (items.length === 0) {
console.log('No data from backend, showing empty chart');
items = [];
}
console.log('Items for chart:', items);
// 按日期排序
items.sort((a: any, b: any) => a.date.localeCompare(b.date));
// 提取日期和数量
const dates = items.map((item: any) => {
// 根据时间维度格式化日期显示
const date = new Date(item.date);
if (timeDimension.value === 'year') {
return `${date.getFullYear()}`;
} else if (timeDimension.value === 'month') {
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
} else {
return `${date.getMonth() + 1}-${String(date.getDate()).padStart(2, '0')}`;
}
});
// 等待所有请求完成
const orderData = await Promise.all(orderDataPromises);
const orderData = items.map((item: any) => item.count);
const amountData = items.map((item: any) => item.amount);
// 计算Y轴最大值
const maxValue = Math.max(...orderData) || 10;
const maxOrderValue = Math.max(...orderData) || 10;
const maxAmountValue = Math.max(...amountData) || 10;
renderEcharts({
grid: {
@@ -61,7 +61,11 @@ onMounted(async () => {
containLabel: true,
left: '1%',
right: '1%',
top: '2%',
top: '10%',
},
legend: {
data: ['订单数', '订单金额'],
top: 0,
},
series: [
{
@@ -77,6 +81,19 @@ onMounted(async () => {
color: '#4f9cff',
},
},
{
data: amountData,
type: 'line',
name: '订单金额',
smooth: true,
itemStyle: {
color: '#52c41a',
},
areaStyle: {
opacity: 0.3,
color: '#52c41a',
},
},
],
tooltip: {
axisPointer: {
@@ -86,8 +103,15 @@ onMounted(async () => {
},
trigger: 'axis',
formatter: (params: any) => {
const param = params[0];
return `${param.axisValue}<br/>${param.seriesName}: ${param.value}`;
let result = `${params[0].axisValue}<br/>`;
params.forEach((param: any) => {
if (param.seriesName === '订单金额') {
result += `${param.seriesName}: ¥${param.value.toFixed(2)}<br/>`;
} else {
result += `${param.seriesName}: ${param.value}<br/>`;
}
});
return result;
},
},
xAxis: {
@@ -95,11 +119,22 @@ onMounted(async () => {
type: 'category',
boundaryGap: false,
},
yAxis: {
max: Math.ceil(maxValue * 1.2), // 比最大值大20%作为Y轴上限
splitNumber: 4,
type: 'value',
},
yAxis: [
{
type: 'value',
name: '订单数',
position: 'left',
max: Math.ceil(maxOrderValue * 1.2),
splitNumber: 4,
},
{
type: 'value',
name: '金额(¥)',
position: 'right',
max: Math.ceil(maxAmountValue * 1.2),
splitNumber: 4,
},
],
});
} catch (error) {
console.error('获取订单趋势数据失败:', error);
@@ -111,7 +146,11 @@ onMounted(async () => {
containLabel: true,
left: '1%',
right: '1%',
top: '2%',
top: '10%',
},
legend: {
data: ['订单数', '订单金额'],
top: 0,
},
series: [
{
@@ -127,6 +166,19 @@ onMounted(async () => {
color: '#4f9cff',
},
},
{
data: Array(30).fill(0),
type: 'line',
name: '订单金额',
smooth: true,
itemStyle: {
color: '#52c41a',
},
areaStyle: {
opacity: 0.3,
color: '#52c41a',
},
},
],
tooltip: {
axisPointer: {
@@ -145,16 +197,66 @@ onMounted(async () => {
type: 'category',
boundaryGap: false,
},
yAxis: {
max: 10,
splitNumber: 4,
type: 'value',
},
yAxis: [
{
type: 'value',
name: '订单数',
position: 'left',
max: 10,
splitNumber: 4,
},
{
type: 'value',
name: '金额(¥)',
position: 'right',
max: 10,
splitNumber: 4,
},
],
});
}
}
// 组件挂载时获取数据
onMounted(() => {
fetchOrderStatistics();
});
// 监听时间维度变化
watch(timeDimension, () => {
fetchOrderStatistics();
});
</script>
<template>
<EchartsUI ref="chartRef" />
</template>
<div>
<div class="mb-4 flex justify-end space-x-2">
<Button
:type="timeDimension === 'day' ? 'primary' : 'default'"
@click="timeDimension = 'day'"
>
</Button>
<Button
:type="timeDimension === 'month' ? 'primary' : 'default'"
@click="timeDimension = 'month'"
>
</Button>
<Button
:type="timeDimension === 'year' ? 'primary' : 'default'"
@click="timeDimension = 'year'"
>
</Button>
<Button
:type="timeDimension === 'all' ? 'primary' : 'default'"
@click="timeDimension = 'all'"
>
全部
</Button>
</div>
<EchartsUI ref="chartRef" />
</div>
</template>

View File

@@ -22,7 +22,7 @@ import AnalyticsVisitsSales from './analytics-visits-sales.vue';
import AnalyticsVisitsSource from './analytics-visits-source.vue';
import AnalyticsVisits from './analytics-visits.vue';
import { getAgentList, getAgentStatistics, getWithdrawalStatistics, getAgentOrderStatistics } from '#/api/agent';
import { getAgentStatistics, getWithdrawalStatistics, getAgentOrderStatistics } from '#/api/agent';
import { getOrderList, getRefundStatistics, getIncomeStatistics } from '#/api/order/order';
import { getPlatformUserList } from '#/api/platform-user';
@@ -63,26 +63,30 @@ const overviewItems = ref<AnalysisOverviewItem[]>([
},
{
icon: SvgBellIcon,
title: '总提现金额',
value: 0,
todaytitle: '今日新增提现金额',
todayValue: 0,
Subtitle: '总退款金额',
SubValue: 0,
todaySubtitle: '今日新增退款金额',
todaySubValue: 0,
title: '总提现金额',
value: 0,
SubValue: 0,
todaySubtitle: '总实际到账金额',
todaySubValue: 0,
extraTitle: '总扣税金额',
extraValue: 0,
todaytitle: '今日新增提现金额',
todayValue: 0,
Subtitle: '总退款金额',
extra2Title: '今日新增退款金额',
extra2Value: 0,
},
]);
const chartTabs: TabOption[] = [
{
label: '推广访问趋势',
value: 'trends',
},
{
label: '订单趋势',
value: 'visits',
},
{
label: '推广访问趋势',
value: 'trends',
},
];
// 获取统计数据
@@ -138,6 +142,8 @@ async function fetchStatistics() {
const withdrawalStatsResponse = await getWithdrawalStatistics();
const totalWithdrawalAmount = withdrawalStatsResponse.total_withdrawal_amount || 0;
const todayWithdrawalAmount = withdrawalStatsResponse.today_withdrawal_amount || 0;
const totalActualAmount = withdrawalStatsResponse.total_actual_amount || 0;
const totalTaxAmount = withdrawalStatsResponse.total_tax_amount || 0;
// 获取退款统计数据
const refundStatsResponse = await getRefundStatistics();
@@ -192,10 +198,14 @@ async function fetchStatistics() {
value: totalWithdrawalAmount,
todaytitle: '今日新增提现金额',
todayValue: todayWithdrawalAmount,
extra2Title: '今日新增退款金额',
extra2Value: todayRefundAmount,
Subtitle: '总退款金额',
SubValue: totalRefundAmount,
todaySubtitle: '今日新增退款金额',
todaySubValue: todayRefundAmount,
todaySubtitle: '总实际到账金额',
todaySubValue: totalActualAmount,
extraTitle: '总扣税金额',
extraValue: totalTaxAmount,
},
];
} catch (error) {
@@ -213,12 +223,12 @@ onMounted(() => {
<div class="p-5">
<AnalysisOverview :items="overviewItems" />
<AnalysisChartsTabs :tabs="chartTabs" class="mt-5">
<template #trends>
<AnalyticsTrends />
</template>
<template #visits>
<AnalyticsVisits />
</template>
<template #trends>
<AnalyticsTrends />
</template>
</AnalysisChartsTabs>
<div class="mt-5 w-full md:flex">