Compare commits
20 Commits
946f60e5c3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d50c57ef0 | |||
| 7634cf8bf1 | |||
| 8f08614a17 | |||
| 3bf38a294e | |||
| 1bba187cf3 | |||
| 90353094ab | |||
| e4b2a1d35a | |||
| f70d33a7d3 | |||
| 1d17926c70 | |||
| d3c32c23fd | |||
| d722860e71 | |||
| 27d63095a9 | |||
| c80d8383c3 | |||
| 26b637eef2 | |||
| 9f511cba43 | |||
| 107b28752c | |||
| 05b568bc93 | |||
| 00523bae4c | |||
| 48a102b0a5 | |||
| 822d92bee4 |
@@ -78,6 +78,7 @@ export namespace AgentApi {
|
|||||||
page: number;
|
page: number;
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
agent_id?: number;
|
agent_id?: number;
|
||||||
|
order_id?: number;
|
||||||
product_name?: string;
|
product_name?: string;
|
||||||
status?: number;
|
status?: number;
|
||||||
}
|
}
|
||||||
@@ -134,15 +135,41 @@ export namespace AgentApi {
|
|||||||
agent_id?: number;
|
agent_id?: number;
|
||||||
status?: number;
|
status?: number;
|
||||||
withdraw_no?: string;
|
withdraw_no?: string;
|
||||||
status?: number;
|
|
||||||
withdraw_no?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提现统计数据
|
// 提现统计数据
|
||||||
export interface WithdrawalStatistics {
|
export interface WithdrawalStatistics {
|
||||||
total_withdrawal_amount: number;
|
total_withdrawal_amount: number;
|
||||||
today_withdrawal_amount: number;
|
today_withdrawal_amount: number;
|
||||||
}
|
total_actual_amount: number;
|
||||||
|
total_tax_amount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 代理订单统计数据
|
||||||
|
export interface AgentOrderStatistics {
|
||||||
|
total_agent_order_count: number; // 总代理订单数
|
||||||
|
today_agent_order_count: number; // 今日代理订单数
|
||||||
|
}
|
||||||
|
|
||||||
|
// 代理统计数据
|
||||||
|
export interface AgentStatistics {
|
||||||
|
total_agent_count: number; // 总代理数
|
||||||
|
today_agent_count: number; // 今日新增代理数
|
||||||
|
}
|
||||||
|
|
||||||
|
// 代理链接产品统计项
|
||||||
|
export interface AgentLinkProductStatisticsItem {
|
||||||
|
product_name: string;
|
||||||
|
link_count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 代理链接产品统计响应
|
||||||
|
export interface AgentLinkProductStatisticsResp {
|
||||||
|
items: AgentLinkProductStatisticsItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 代理链接产品统计请求参数
|
||||||
|
export interface GetAgentLinkProductStatisticsParams {}
|
||||||
|
|
||||||
// 代理上级抽佣相关接口
|
// 代理上级抽佣相关接口
|
||||||
export interface AgentCommissionDeductionListItem {
|
export interface AgentCommissionDeductionListItem {
|
||||||
@@ -307,6 +334,69 @@ export namespace AgentApi {
|
|||||||
price_ratio?: null | number; // 提价区间收取比例
|
price_ratio?: null | number; // 提价区间收取比例
|
||||||
price_increase_amount?: null | number; // 在原本成本上加价的金额
|
price_increase_amount?: null | number; // 在原本成本上加价的金额
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 代理钱包信息
|
||||||
|
export interface AgentWalletInfo {
|
||||||
|
balance: number; // 可用余额
|
||||||
|
frozen_balance: number; // 冻结余额
|
||||||
|
total_earnings: number; // 总收益
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改代理钱包余额请求
|
||||||
|
export interface UpdateAgentWalletBalanceReq {
|
||||||
|
agent_id: number; // 代理ID
|
||||||
|
amount: number; // 修改金额(正数增加,负数减少)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改代理钱包余额响应
|
||||||
|
export interface UpdateAgentWalletBalanceResp {
|
||||||
|
success: boolean; // 是否成功
|
||||||
|
balance: number; // 修改后的余额
|
||||||
|
}
|
||||||
|
|
||||||
|
// 代理钱包流水相关接口
|
||||||
|
export interface WalletTransactionListItem {
|
||||||
|
id: number;
|
||||||
|
agent_id: number;
|
||||||
|
transaction_type: string;
|
||||||
|
amount: number;
|
||||||
|
balance_before: number;
|
||||||
|
balance_after: number;
|
||||||
|
frozen_balance_before: number;
|
||||||
|
frozen_balance_after: number;
|
||||||
|
transaction_id?: string;
|
||||||
|
related_user_id?: number;
|
||||||
|
remark?: string;
|
||||||
|
create_time: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WalletTransactionList {
|
||||||
|
total: number;
|
||||||
|
items: WalletTransactionListItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetWalletTransactionListParams {
|
||||||
|
page: number;
|
||||||
|
pageSize: number;
|
||||||
|
agent_id: number;
|
||||||
|
transaction_type?: string;
|
||||||
|
create_time_start?: string;
|
||||||
|
create_time_end?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 系统配置相关接口
|
||||||
|
export interface SystemConfig {
|
||||||
|
commission_safe_mode: boolean; // 佣金安全防御模式
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateSystemConfigReq {
|
||||||
|
commission_safe_mode: boolean; // 佣金安全防御模式:true-冻结模式,false-直接结算模式
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateSystemConfigResp {
|
||||||
|
success: boolean; // 是否成功
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -342,6 +432,37 @@ async function getAgentCommissionList(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新代理佣金状态
|
||||||
|
*/
|
||||||
|
async function updateAgentCommissionStatus(
|
||||||
|
id: number,
|
||||||
|
status: number,
|
||||||
|
) {
|
||||||
|
return requestClient.post<{ success: boolean }>(
|
||||||
|
'/agent/agent-commission/update-status',
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
status,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量解冻代理佣金
|
||||||
|
*/
|
||||||
|
async function batchUnfreezeAgentCommission(
|
||||||
|
agentId?: number,
|
||||||
|
) {
|
||||||
|
return requestClient.post<{
|
||||||
|
success: boolean;
|
||||||
|
count: number;
|
||||||
|
amount: number;
|
||||||
|
}>('/agent/agent-commission/batch-unfreeze', {
|
||||||
|
agent_id: agentId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取代理奖励列表
|
* 获取代理奖励列表
|
||||||
*/
|
*/
|
||||||
@@ -487,17 +608,108 @@ async function getWithdrawalStatistics() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取代理订单统计数据
|
||||||
|
*/
|
||||||
|
async function getAgentOrderStatistics() {
|
||||||
|
return requestClient.get<AgentApi.AgentOrderStatistics>(
|
||||||
|
'/agent/agent-order/statistics',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取代理统计数据
|
||||||
|
*/
|
||||||
|
async function getAgentStatistics() {
|
||||||
|
return requestClient.get<AgentApi.AgentStatistics>(
|
||||||
|
'/agent/statistics',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取代理链接产品统计数据
|
||||||
|
*/
|
||||||
|
async function getAgentLinkProductStatistics() {
|
||||||
|
return requestClient.get<AgentApi.AgentLinkProductStatisticsResp>(
|
||||||
|
'/agent/agent-link/product-statistics',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取代理钱包信息
|
||||||
|
*/
|
||||||
|
async function getAgentWallet(agentId: number) {
|
||||||
|
return requestClient.get<AgentApi.AgentWalletInfo>(
|
||||||
|
`/agent/wallet/${agentId}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改代理钱包余额
|
||||||
|
*/
|
||||||
|
async function updateAgentWalletBalance(params: AgentApi.UpdateAgentWalletBalanceReq) {
|
||||||
|
return requestClient.post<AgentApi.UpdateAgentWalletBalanceResp>(
|
||||||
|
'/agent/wallet/update-balance',
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取系统配置
|
||||||
|
*/
|
||||||
|
async function getSystemConfig() {
|
||||||
|
return requestClient.get<AgentApi.SystemConfig>(
|
||||||
|
'/agent/system-config',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新系统配置
|
||||||
|
*/
|
||||||
|
async function updateSystemConfig(params: AgentApi.UpdateSystemConfigReq) {
|
||||||
|
return requestClient.post<AgentApi.UpdateSystemConfigResp>(
|
||||||
|
'/agent/system-config',
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取代理钱包流水列表
|
||||||
|
*/
|
||||||
|
async function getWalletTransactionList(
|
||||||
|
params: AgentApi.GetWalletTransactionListParams,
|
||||||
|
) {
|
||||||
|
return requestClient.get<AgentApi.WalletTransactionList>(
|
||||||
|
'/agent/wallet-transaction/list',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
batchUnfreezeAgentCommission,
|
||||||
getAgentCommissionDeductionList,
|
getAgentCommissionDeductionList,
|
||||||
getAgentCommissionList,
|
getAgentCommissionList,
|
||||||
getAgentLinkList,
|
getAgentLinkList,
|
||||||
|
getAgentLinkProductStatistics,
|
||||||
getAgentList,
|
getAgentList,
|
||||||
getAgentMembershipConfigList,
|
getAgentMembershipConfigList,
|
||||||
|
getAgentOrderStatistics,
|
||||||
getAgentPlatformDeductionList,
|
getAgentPlatformDeductionList,
|
||||||
getAgentProductionConfigList,
|
getAgentProductionConfigList,
|
||||||
getAgentRewardList,
|
getAgentRewardList,
|
||||||
|
getAgentStatistics,
|
||||||
|
getAgentWallet,
|
||||||
getAgentWithdrawalList,
|
getAgentWithdrawalList,
|
||||||
getMembershipRechargeOrderList,
|
getMembershipRechargeOrderList,
|
||||||
|
getWithdrawalStatistics,
|
||||||
|
reviewBankCardWithdrawal,
|
||||||
|
updateAgentCommissionStatus,
|
||||||
updateAgentMembershipConfig,
|
updateAgentMembershipConfig,
|
||||||
updateAgentProductionConfig,
|
updateAgentProductionConfig,
|
||||||
|
updateAgentWalletBalance,
|
||||||
|
getSystemConfig,
|
||||||
|
updateSystemConfig,
|
||||||
|
getWalletTransactionList,
|
||||||
};
|
};
|
||||||
|
|||||||
28
apps/web-antd/src/api/order/order-statistics.ts
Normal file
28
apps/web-antd/src/api/order/order-statistics.ts
Normal 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 }
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -49,6 +49,12 @@ export namespace OrderApi {
|
|||||||
total_profit_amount: number;
|
total_profit_amount: number;
|
||||||
today_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');
|
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 };
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export namespace PlatformUserApi {
|
|||||||
nickname: string;
|
nickname: string;
|
||||||
info: string;
|
info: string;
|
||||||
inside: number;
|
inside: number;
|
||||||
|
disable: number; // 0 可用 1 禁用
|
||||||
create_time: string;
|
create_time: string;
|
||||||
update_time: string;
|
update_time: string;
|
||||||
}
|
}
|
||||||
@@ -19,10 +20,11 @@ export namespace PlatformUserApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdatePlatformUserRequest {
|
export interface UpdatePlatformUserRequest {
|
||||||
mobile: string;
|
mobile?: string;
|
||||||
nickname: string;
|
nickname?: string;
|
||||||
info: string;
|
info?: string;
|
||||||
inside: number;
|
inside?: number;
|
||||||
|
disable?: number; // 0 可用 1 禁用
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import type { VbenFormSchema } from '#/adapter/form';
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
import type { AgentApi } from '#/api';
|
||||||
|
|
||||||
// 佣金记录列表列配置
|
// 佣金记录列表列配置
|
||||||
export function useCommissionColumns(): VxeTableGridOptions['columns'] {
|
export function useCommissionColumns(
|
||||||
|
onActionClick?: (params: { code: string; row: AgentApi.AgentCommissionListItem }) => void,
|
||||||
|
): VxeTableGridOptions['columns'] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
field: 'agent_id',
|
field: 'agent_id',
|
||||||
@@ -32,9 +35,9 @@ export function useCommissionColumns(): VxeTableGridOptions['columns'] {
|
|||||||
width: 100,
|
width: 100,
|
||||||
formatter: ({ cellValue }: { cellValue: number }) => {
|
formatter: ({ cellValue }: { cellValue: number }) => {
|
||||||
const statusMap: Record<number, string> = {
|
const statusMap: Record<number, string> = {
|
||||||
0: '待结算',
|
0: '已结算',
|
||||||
1: '已结算',
|
1: '冻结中',
|
||||||
2: '已取消',
|
2: '已退款',
|
||||||
};
|
};
|
||||||
return statusMap[cellValue] || '未知';
|
return statusMap[cellValue] || '未知';
|
||||||
},
|
},
|
||||||
@@ -46,16 +49,73 @@ export function useCommissionColumns(): VxeTableGridOptions['columns'] {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
sortType: 'string' as const,
|
sortType: 'string' as const,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
align: 'center',
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellOperation',
|
||||||
|
attrs: {
|
||||||
|
nameField: 'id',
|
||||||
|
nameTitle: '操作',
|
||||||
|
onClick: onActionClick,
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
code: 'freeze',
|
||||||
|
text: '冻结',
|
||||||
|
type: 'warning',
|
||||||
|
disabled: (row: AgentApi.AgentCommissionListItem) =>
|
||||||
|
row?.status !== 0,
|
||||||
|
class: (row: AgentApi.AgentCommissionListItem) =>
|
||||||
|
row?.status !== 0 ? '!text-gray-400 !cursor-not-allowed' : '',
|
||||||
|
tooltip: (row: AgentApi.AgentCommissionListItem) => {
|
||||||
|
if (row?.status === 1) return '该佣金已处于冻结中';
|
||||||
|
if (row?.status === 2) return '已取消的佣金无法操作';
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: 'unfreeze',
|
||||||
|
text: '解冻',
|
||||||
|
type: 'primary',
|
||||||
|
disabled: (row: AgentApi.AgentCommissionListItem) =>
|
||||||
|
row?.status !== 1,
|
||||||
|
class: (row: AgentApi.AgentCommissionListItem) =>
|
||||||
|
row?.status !== 1 ? '!text-gray-400 !cursor-not-allowed' : '',
|
||||||
|
tooltip: (row: AgentApi.AgentCommissionListItem) => {
|
||||||
|
if (row?.status === 0) return '已结算的佣金无需解冻';
|
||||||
|
if (row?.status === 2) return '已退款的佣金无法操作';
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
field: 'operation',
|
||||||
|
fixed: 'right',
|
||||||
|
title: '操作',
|
||||||
|
width: 300,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 佣金记录搜索表单配置
|
// 佣金记录搜索表单配置
|
||||||
export function useCommissionFormSchema(): VbenFormSchema[] {
|
export function useCommissionFormSchema(): VbenFormSchema[] {
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
component: 'InputNumber',
|
||||||
|
fieldName: 'order_id',
|
||||||
|
label: '订单ID',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入订单ID',
|
||||||
|
style: { width: '100%' },
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
fieldName: 'product_name',
|
fieldName: 'product_name',
|
||||||
label: '产品名称',
|
label: '产品名称',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入产品名称(支持模糊搜索)',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Select',
|
component: 'Select',
|
||||||
@@ -63,10 +123,11 @@ export function useCommissionFormSchema(): VbenFormSchema[] {
|
|||||||
label: '状态',
|
label: '状态',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
|
placeholder: '请选择状态',
|
||||||
options: [
|
options: [
|
||||||
{ label: '待结算', value: 0 },
|
{ label: '已结算', value: 0 },
|
||||||
{ label: '已结算', value: 1 },
|
{ label: '冻结中', value: 1 },
|
||||||
{ label: '已取消', value: 2 },
|
{ label: '已退款', value: 2 },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,10 +1,20 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed, h, onMounted, ref } from 'vue';
|
||||||
|
|
||||||
import { Page } from '@vben/common-ui';
|
import { Page } from '@vben/common-ui';
|
||||||
|
import { Button, message, Modal, Select, Switch, Tooltip } from 'ant-design-vue';
|
||||||
|
|
||||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
import { getAgentCommissionList } from '#/api/agent';
|
import {
|
||||||
|
batchUnfreezeAgentCommission,
|
||||||
|
getAgentCommissionList,
|
||||||
|
getAgentList,
|
||||||
|
getAgentWallet,
|
||||||
|
updateAgentCommissionStatus,
|
||||||
|
getSystemConfig,
|
||||||
|
updateSystemConfig,
|
||||||
|
} from '#/api/agent';
|
||||||
|
import type { AgentApi } from '#/api';
|
||||||
|
|
||||||
import { useCommissionColumns, useCommissionFormSchema } from './data';
|
import { useCommissionColumns, useCommissionFormSchema } from './data';
|
||||||
|
|
||||||
@@ -12,37 +22,371 @@ interface Props {
|
|||||||
agentId?: number;
|
agentId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface QueryParams {
|
|
||||||
currentPage: number;
|
|
||||||
pageSize: number;
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
// 用于一键解冻筛选的代理商ID
|
||||||
|
const unfreezeAgentId = ref<number | undefined>();
|
||||||
|
|
||||||
|
// 佣金安全防御模式配置
|
||||||
|
const commissionSafeMode = ref<boolean>(false);
|
||||||
|
const safeModeLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
// 代理商列表(完整列表)
|
||||||
|
const allAgentList = ref<AgentApi.AgentListItem[]>([]);
|
||||||
|
|
||||||
|
// 显示在下拉框中的代理商列表(可能是过滤后的)
|
||||||
|
const agentList = ref<AgentApi.AgentListItem[]>([]);
|
||||||
|
|
||||||
const queryParams = computed(() => ({
|
const queryParams = computed(() => ({
|
||||||
...(props.agentId ? { agent_id: props.agentId } : {}),
|
...(props.agentId ? { agent_id: props.agentId } : {}),
|
||||||
|
...(unfreezeAgentId.value ? { agent_id: unfreezeAgentId.value } : {}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const [Grid] = useVbenVxeGrid({
|
// 加载代理商列表
|
||||||
|
async function loadAgentList() {
|
||||||
|
try {
|
||||||
|
const result = await getAgentList({ page: 1, pageSize: 10000 });
|
||||||
|
allAgentList.value = result.items || [];
|
||||||
|
agentList.value = result.items || [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载代理商列表失败:', error);
|
||||||
|
message.error('加载代理商列表失败,请刷新页面重试');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索时动态过滤代理商(支持ID和手机号)
|
||||||
|
function onAgentSearch(value: string) {
|
||||||
|
if (!value || value.trim() === '') {
|
||||||
|
// 如果输入为空,显示所有代理商
|
||||||
|
agentList.value = allAgentList.value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchValue = value.trim();
|
||||||
|
|
||||||
|
// 从完整列表中过滤匹配的代理商
|
||||||
|
const filtered = allAgentList.value.filter(agent => {
|
||||||
|
// 匹配代理ID
|
||||||
|
if (agent.id.toString().includes(searchValue)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 匹配手机号
|
||||||
|
if (agent.mobile && agent.mobile.includes(searchValue)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 匹配姓名(如果存在)
|
||||||
|
if (agent.real_name && agent.real_name.includes(searchValue)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
agentList.value = filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取未找到时的提示文案
|
||||||
|
function getNotFoundContent() {
|
||||||
|
if (unfreezeAgentId.value) {
|
||||||
|
return '点击确认使用此ID';
|
||||||
|
}
|
||||||
|
return '暂无代理商';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面加载时获取代理商列表和系统配置
|
||||||
|
onMounted(async () => {
|
||||||
|
loadAgentList();
|
||||||
|
await loadSystemConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 加载系统配置
|
||||||
|
async function loadSystemConfig() {
|
||||||
|
try {
|
||||||
|
const config = await getSystemConfig();
|
||||||
|
commissionSafeMode.value = config.commission_safe_mode;
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('加载系统配置失败:', error);
|
||||||
|
message.error('加载系统配置失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换安全防御模式
|
||||||
|
async function onSafeModeChange(checked: boolean | string | number) {
|
||||||
|
const isChecked = Boolean(checked);
|
||||||
|
safeModeLoading.value = true;
|
||||||
|
try {
|
||||||
|
await updateSystemConfig({ commission_safe_mode: isChecked });
|
||||||
|
commissionSafeMode.value = isChecked;
|
||||||
|
message.success(`佣金安全防御模式已${isChecked ? '开启' : '关闭'}`);
|
||||||
|
} catch (error: any) {
|
||||||
|
const errorMsg = error?.response?.data?.msg || error?.message || '操作失败,请重试';
|
||||||
|
message.error(errorMsg);
|
||||||
|
// 恢复原状态
|
||||||
|
commissionSafeMode.value = !isChecked;
|
||||||
|
} finally {
|
||||||
|
safeModeLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 操作处理函数
|
||||||
|
function onActionClick({ code, row }: { code: string; row: any }) {
|
||||||
|
switch (code) {
|
||||||
|
case 'freeze':
|
||||||
|
onFreeze(row);
|
||||||
|
break;
|
||||||
|
case 'unfreeze':
|
||||||
|
onUnfreeze(row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 冻结佣金
|
||||||
|
async function onFreeze(row: any) {
|
||||||
|
try {
|
||||||
|
// 先获取代理商钱包信息,检查余额是否充足
|
||||||
|
const hideChecking = message.loading({
|
||||||
|
content: '正在检查余额...',
|
||||||
|
duration: 0,
|
||||||
|
key: 'check_balance',
|
||||||
|
});
|
||||||
|
|
||||||
|
const wallet = await getAgentWallet(row.agent_id);
|
||||||
|
hideChecking();
|
||||||
|
|
||||||
|
// 检查余额是否充足
|
||||||
|
if (wallet.balance < row.amount) {
|
||||||
|
Modal.warning({
|
||||||
|
title: '余额不足',
|
||||||
|
content: `该代理商当前可用余额为 ¥${wallet.balance.toFixed(2)},不足以冻结佣金 ¥${row.amount.toFixed(2)}。\n缺少金额:¥${(row.amount - wallet.balance).toFixed(2)}`,
|
||||||
|
okText: '我知道了',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 余额充足,继续冻结操作
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确认冻结',
|
||||||
|
content: `确定要冻结佣金金额 ¥${row.amount.toFixed(2)} 吗?\n当前可用余额:¥${wallet.balance.toFixed(2)},冻结后可用余额:¥${(wallet.balance - row.amount).toFixed(2)}`,
|
||||||
|
okText: '确认冻结',
|
||||||
|
cancelText: '取消',
|
||||||
|
onOk: async () => {
|
||||||
|
try {
|
||||||
|
await updateAgentCommissionStatus(row.id, 1);
|
||||||
|
message.success('佣金已冻结');
|
||||||
|
onRefresh();
|
||||||
|
} catch (error: any) {
|
||||||
|
const errorMsg = error?.response?.data?.msg || error?.message || '操作失败,请重试';
|
||||||
|
message.error(errorMsg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error: any) {
|
||||||
|
const errorMsg = error?.response?.data?.msg || error?.message || '获取钱包信息失败,请重试';
|
||||||
|
message.error(errorMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解冻佣金到用户余额
|
||||||
|
async function onUnfreeze(row: any) {
|
||||||
|
try {
|
||||||
|
// 获取代理商钱包信息用于显示
|
||||||
|
const hideChecking = message.loading({
|
||||||
|
content: '正在获取钱包信息...',
|
||||||
|
duration: 0,
|
||||||
|
key: 'get_wallet',
|
||||||
|
});
|
||||||
|
|
||||||
|
const wallet = await getAgentWallet(row.agent_id);
|
||||||
|
hideChecking();
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确认解冻',
|
||||||
|
content: `确定要解冻佣金金额 ¥${row.amount.toFixed(2)} 吗?\n解冻后将转入用户钱包余额。\n当前可用余额:¥${wallet.balance.toFixed(2)},解冻后可用余额:¥${(wallet.balance + row.amount).toFixed(2)}`,
|
||||||
|
okText: '确认解冻',
|
||||||
|
cancelText: '取消',
|
||||||
|
onOk: async () => {
|
||||||
|
try {
|
||||||
|
await updateAgentCommissionStatus(row.id, 0);
|
||||||
|
message.success('佣金已解冻并转入用户钱包余额');
|
||||||
|
onRefresh();
|
||||||
|
} catch (error: any) {
|
||||||
|
const errorMsg = error?.response?.data?.msg || error?.message || '操作失败,请重试';
|
||||||
|
message.error(errorMsg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error: any) {
|
||||||
|
const errorMsg = error?.response?.data?.msg || error?.message || '获取钱包信息失败,请重试';
|
||||||
|
message.error(errorMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新列表
|
||||||
|
function onRefresh() {
|
||||||
|
gridApi.query();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选中代理商后刷新表格
|
||||||
|
function onAgentSelect(value: any) {
|
||||||
|
console.log('选中代理商:', value);
|
||||||
|
// 如果是清空选择,value 为 undefined
|
||||||
|
// 如果有值,转换为数字
|
||||||
|
unfreezeAgentId.value = value ? parseInt(String(value), 10) : undefined;
|
||||||
|
// 延迟一点让 computed 更新后再刷新
|
||||||
|
setTimeout(() => {
|
||||||
|
onRefresh();
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量解冻佣金
|
||||||
|
async function onBatchUnfreeze() {
|
||||||
|
const targetAgentId = unfreezeAgentId.value || props.agentId;
|
||||||
|
|
||||||
|
const hideChecking = message.loading({
|
||||||
|
content: '正在检查数据,请稍候...',
|
||||||
|
duration: 0,
|
||||||
|
key: 'check_frozen_data',
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 查询所有冻结中的佣金记录
|
||||||
|
const frozenCommissions = await getAgentCommissionList({
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10000,
|
||||||
|
status: 1,
|
||||||
|
...(targetAgentId ? { agent_id: targetAgentId } : {}),
|
||||||
|
});
|
||||||
|
|
||||||
|
hideChecking();
|
||||||
|
|
||||||
|
// 如果没有冻结的佣金,直接返回
|
||||||
|
if (!frozenCommissions.items || frozenCommissions.items.length === 0) {
|
||||||
|
message.info({
|
||||||
|
content: '没有需要解冻的冻结佣金',
|
||||||
|
key: 'check_frozen_data',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 统计每个代理商的佣金冻结金额
|
||||||
|
const commissionFrozenMap = new Map<number, number>();
|
||||||
|
frozenCommissions.items.forEach((item: any) => {
|
||||||
|
const currentAmount = commissionFrozenMap.get(item.agent_id) || 0;
|
||||||
|
commissionFrozenMap.set(item.agent_id, currentAmount + item.amount);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. 检查每个代理商的钱包冻结余额是否足够
|
||||||
|
let insufficientAgents: string[] = [];
|
||||||
|
let totalFrozenAmount = 0;
|
||||||
|
|
||||||
|
for (const [agentId, commissionAmount] of commissionFrozenMap) {
|
||||||
|
totalFrozenAmount += commissionAmount;
|
||||||
|
|
||||||
|
// 获取该代理商的钱包信息
|
||||||
|
const wallet = await getAgentWallet(agentId);
|
||||||
|
|
||||||
|
if (wallet.frozen_balance < commissionAmount) {
|
||||||
|
// 找到该代理商的信息
|
||||||
|
const agent = allAgentList.value.find(a => a.id === agentId);
|
||||||
|
const agentInfo = agent ? `${agent.real_name || agent.mobile} (ID: ${agentId})` : `ID: ${agentId}`;
|
||||||
|
insufficientAgents.push(
|
||||||
|
`${agentInfo}:钱包冻结余额 ¥${wallet.frozen_balance.toFixed(2)},需要解冻 ¥${commissionAmount.toFixed(2)},缺少 ¥${(commissionAmount - wallet.frozen_balance).toFixed(2)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有余额不足的代理商,显示错误信息
|
||||||
|
if (insufficientAgents.length > 0) {
|
||||||
|
Modal.error({
|
||||||
|
title: '冻结余额不足,无法解冻',
|
||||||
|
width: 600,
|
||||||
|
content: h('div', [
|
||||||
|
h('p', '以下代理商的钱包冻结余额不足以解冻其冻结的佣金:'),
|
||||||
|
h('ul', { style: { 'max-height': '300px', 'overflow-y': 'auto', 'padding-left': '20px' } },
|
||||||
|
insufficientAgents.map(msg => h('li', { style: { marginBottom: '8px' } }, msg))
|
||||||
|
),
|
||||||
|
h('p', { style: { marginTop: '16px', color: '#999' } }, '请先核实钱包冻结余额数据,或联系技术支持。'),
|
||||||
|
]),
|
||||||
|
okText: '我知道了',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 余额检查通过,确认解冻
|
||||||
|
const content = targetAgentId
|
||||||
|
? `确定要一键解冻代理商 ID: ${targetAgentId} 所有冻结中的佣金吗?\n\n共 ${frozenCommissions.items.length} 条记录,总金额 ¥${totalFrozenAmount.toFixed(2)}。\n解冻后将全部转入用户钱包余额。`
|
||||||
|
: `确定要一键解冻所有冻结中的佣金吗?\n\n共 ${frozenCommissions.items.length} 条记录,总金额 ¥${totalFrozenAmount.toFixed(2)}。\n解冻后将全部转入用户钱包余额。`;
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: '批量解冻确认',
|
||||||
|
content,
|
||||||
|
okText: '确认解冻',
|
||||||
|
cancelText: '取消',
|
||||||
|
onOk: async () => {
|
||||||
|
const hideLoading = message.loading({
|
||||||
|
content: '正在批量解冻佣金,请稍候...',
|
||||||
|
duration: 0,
|
||||||
|
key: 'batch_unfreeze',
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const result = await batchUnfreezeAgentCommission(targetAgentId);
|
||||||
|
message.success({
|
||||||
|
content: `批量解冻成功!共解冻 ${result.count} 条记录,总金额 ¥${result.amount.toFixed(2)}`,
|
||||||
|
key: 'batch_unfreeze',
|
||||||
|
});
|
||||||
|
onRefresh();
|
||||||
|
} catch (error: any) {
|
||||||
|
hideLoading();
|
||||||
|
const errorMsg = error?.response?.data?.msg || error?.message || '批量解冻失败,请重试';
|
||||||
|
// 如果是版本冲突错误,给出更友好的提示
|
||||||
|
if (errorMsg.includes('update db no rows change') || errorMsg.includes('版本冲突') || errorMsg.includes('状态已被其他操作修改')) {
|
||||||
|
message.error({
|
||||||
|
content: '批量解冻失败:部分佣金或钱包数据已被其他操作修改,请稍后重试。如果问题持续,请联系管理员。',
|
||||||
|
key: 'batch_unfreeze',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `批量解冻失败:${errorMsg}`,
|
||||||
|
key: 'batch_unfreeze',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error: any) {
|
||||||
|
hideChecking();
|
||||||
|
const errorMsg = error?.response?.data?.msg || error?.message || '数据检查失败,请重试';
|
||||||
|
message.error({
|
||||||
|
content: `检查失败:${errorMsg}`,
|
||||||
|
key: 'check_frozen_data',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
formOptions: {
|
formOptions: {
|
||||||
schema: useCommissionFormSchema(),
|
schema: useCommissionFormSchema(),
|
||||||
submitOnChange: true,
|
submitOnChange: true,
|
||||||
},
|
},
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
columns: useCommissionColumns(),
|
columns: useCommissionColumns(onActionClick),
|
||||||
|
height: 'auto',
|
||||||
|
keepSource: true,
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
custom: true,
|
||||||
|
export: false,
|
||||||
|
refresh: { code: 'query' },
|
||||||
|
search: true,
|
||||||
|
zoom: true,
|
||||||
|
},
|
||||||
proxyConfig: {
|
proxyConfig: {
|
||||||
ajax: {
|
ajax: {
|
||||||
query: async ({
|
query: async ({ page, form, sort }: any, formValues: Record<string, any>) => {
|
||||||
page,
|
|
||||||
form,
|
|
||||||
}: {
|
|
||||||
form: Record<string, any>;
|
|
||||||
page: QueryParams;
|
|
||||||
}) => {
|
|
||||||
return await getAgentCommissionList({
|
return await getAgentCommissionList({
|
||||||
...queryParams.value,
|
...queryParams.value,
|
||||||
...form,
|
...formValues,
|
||||||
page: page.currentPage,
|
page: page.currentPage,
|
||||||
pageSize: page.pageSize,
|
pageSize: page.pageSize,
|
||||||
});
|
});
|
||||||
@@ -52,6 +396,7 @@ const [Grid] = useVbenVxeGrid({
|
|||||||
result: 'items',
|
result: 'items',
|
||||||
total: 'total',
|
total: 'total',
|
||||||
},
|
},
|
||||||
|
autoLoad: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -59,6 +404,53 @@ const [Grid] = useVbenVxeGrid({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page :auto-content-height="!agentId">
|
<Page :auto-content-height="!agentId">
|
||||||
<Grid :table-title="agentId ? '佣金记录列表' : '所有佣金记录'" />
|
<Grid :table-title="agentId ? '佣金记录列表' : '所有佣金记录'">
|
||||||
|
<template #toolbar-tools>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<Tooltip placement="top" title="开启后,佣金结算时将先冻结到钱包冻结余额,需要手动解冻才能使用;关闭后,佣金将直接结算到可用余额">
|
||||||
|
<div class="flex items-center gap-2 mr-4">
|
||||||
|
<span class="text-sm text-gray-600">安全防御机制:</span>
|
||||||
|
<Switch
|
||||||
|
v-model:checked="commissionSafeMode"
|
||||||
|
:loading="safeModeLoading"
|
||||||
|
checked-children="开启"
|
||||||
|
un-checked-children="关闭"
|
||||||
|
@change="onSafeModeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
<div class="w-px h-6 bg-gray-300 mx-2"></div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span class="text-sm text-gray-600">选择代理商:</span>
|
||||||
|
<Select
|
||||||
|
v-model:value="unfreezeAgentId"
|
||||||
|
placeholder="全部代理商 / 输入代理ID或手机号"
|
||||||
|
:allow-clear="true"
|
||||||
|
:loading="agentList.length === 0"
|
||||||
|
style="width: 260px"
|
||||||
|
show-search
|
||||||
|
:filter-option="false"
|
||||||
|
:show-arrow="true"
|
||||||
|
:not-found-content="getNotFoundContent()"
|
||||||
|
@search="onAgentSearch"
|
||||||
|
@change="onAgentSelect"
|
||||||
|
>
|
||||||
|
<Select.Option
|
||||||
|
v-for="agent in agentList"
|
||||||
|
:key="agent.id"
|
||||||
|
:value="agent.id"
|
||||||
|
:label="`${agent.real_name || agent.mobile} (ID: ${agent.id})`"
|
||||||
|
>
|
||||||
|
{{ agent.real_name || agent.mobile }} (ID: {{ agent.id }})
|
||||||
|
</Select.Option>
|
||||||
|
</Select>
|
||||||
|
<Button type="primary" @click="onBatchUnfreeze">
|
||||||
|
<span class="mr-1">⚡</span>
|
||||||
|
一键解冻
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -19,11 +19,14 @@ import { getAgentList } from '#/api/agent';
|
|||||||
import { useColumns, useGridFormSchema } from './data';
|
import { useColumns, useGridFormSchema } from './data';
|
||||||
import CommissionDeductionModal from './modules/commission-deduction-modal.vue';
|
import CommissionDeductionModal from './modules/commission-deduction-modal.vue';
|
||||||
import CommissionModal from './modules/commission-modal.vue';
|
import CommissionModal from './modules/commission-modal.vue';
|
||||||
|
import CommissionHistoryModal from './modules/commission-history-modal.vue';
|
||||||
import Form from './modules/form.vue';
|
import Form from './modules/form.vue';
|
||||||
import LinkModal from './modules/link-modal.vue';
|
import LinkModal from './modules/link-modal.vue';
|
||||||
import PlatformDeductionModal from './modules/platform-deduction-modal.vue';
|
import PlatformDeductionModal from './modules/platform-deduction-modal.vue';
|
||||||
import RewardModal from './modules/reward-modal.vue';
|
import RewardModal from './modules/reward-modal.vue';
|
||||||
import WithdrawalModal from './modules/withdrawal-modal.vue';
|
import WithdrawalModal from './modules/withdrawal-modal.vue';
|
||||||
|
import BalanceModal from './modules/balance-modal.vue';
|
||||||
|
import WalletTransactionModal from './modules/wallet-transaction-modal.vue';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -72,6 +75,24 @@ const [PlatformDeductionModalComponent, platformDeductionModalApi] =
|
|||||||
destroyOnClose: true,
|
destroyOnClose: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 修改余额弹窗
|
||||||
|
const [BalanceModalComponent, balanceModalApi] = useVbenModal({
|
||||||
|
connectedComponent: BalanceModal,
|
||||||
|
destroyOnClose: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 钱包流水记录弹窗
|
||||||
|
const [WalletTransactionModalComponent, walletTransactionModalApi] = useVbenModal({
|
||||||
|
connectedComponent: WalletTransactionModal,
|
||||||
|
destroyOnClose: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 历史佣金记录弹窗
|
||||||
|
const [CommissionHistoryModalComponent, commissionHistoryModalApi] = useVbenModal({
|
||||||
|
connectedComponent: CommissionHistoryModal,
|
||||||
|
destroyOnClose: true,
|
||||||
|
});
|
||||||
|
|
||||||
// 表格配置
|
// 表格配置
|
||||||
const [Grid, gridApi] = useVbenVxeGrid({
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
formOptions: {
|
formOptions: {
|
||||||
@@ -144,10 +165,23 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
|
|
||||||
// 更多操作菜单项
|
// 更多操作菜单项
|
||||||
const moreMenuItems = [
|
const moreMenuItems = [
|
||||||
|
{
|
||||||
|
key: 'update-balance',
|
||||||
|
label: '修改余额',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'commission-history',
|
||||||
|
label: '历史佣金记录',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'wallet-transaction',
|
||||||
|
label: '钱包流水记录',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'links',
|
key: 'links',
|
||||||
label: '推广链接',
|
label: '推广链接',
|
||||||
},
|
},
|
||||||
|
|
||||||
// {
|
// {
|
||||||
// key: 'commission',
|
// key: 'commission',
|
||||||
// label: '佣金记录',
|
// label: '佣金记录',
|
||||||
@@ -194,6 +228,10 @@ function onActionClick(
|
|||||||
onViewCommission(e.row);
|
onViewCommission(e.row);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'commission-history': {
|
||||||
|
onViewCommissionHistory(e.row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'commission-deduction': {
|
case 'commission-deduction': {
|
||||||
onViewCommissionDeduction(e.row);
|
onViewCommissionDeduction(e.row);
|
||||||
break;
|
break;
|
||||||
@@ -214,6 +252,10 @@ function onActionClick(
|
|||||||
onViewReward(e.row);
|
onViewReward(e.row);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'update-balance': {
|
||||||
|
onUpdateBalance(e.row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'view-sub-agent': {
|
case 'view-sub-agent': {
|
||||||
router.replace({
|
router.replace({
|
||||||
query: {
|
query: {
|
||||||
@@ -227,6 +269,10 @@ function onActionClick(
|
|||||||
onViewWithdrawal(e.row);
|
onViewWithdrawal(e.row);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'wallet-transaction': {
|
||||||
|
onViewWalletTransaction(e.row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +286,16 @@ function onViewLinks(row: AgentApi.AgentListItem) {
|
|||||||
linkModalApi.setData({ agentId: row.id }).open();
|
linkModalApi.setData({ agentId: row.id }).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 修改余额
|
||||||
|
function onUpdateBalance(row: AgentApi.AgentListItem) {
|
||||||
|
balanceModalApi.setData({ agentId: row.id }).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看钱包流水记录
|
||||||
|
function onViewWalletTransaction(row: AgentApi.AgentListItem) {
|
||||||
|
walletTransactionModalApi.setData({ agentId: row.id }).open();
|
||||||
|
}
|
||||||
|
|
||||||
// 查看佣金记录
|
// 查看佣金记录
|
||||||
function onViewCommission(row: AgentApi.AgentListItem) {
|
function onViewCommission(row: AgentApi.AgentListItem) {
|
||||||
commissionModalApi.setData({ agentId: row.id }).open();
|
commissionModalApi.setData({ agentId: row.id }).open();
|
||||||
@@ -265,6 +321,11 @@ function onViewPlatformDeduction(row: AgentApi.AgentListItem) {
|
|||||||
platformDeductionModalApi.setData({ agentId: row.id }).open();
|
platformDeductionModalApi.setData({ agentId: row.id }).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查看历史佣金记录
|
||||||
|
function onViewCommissionHistory(row: AgentApi.AgentListItem) {
|
||||||
|
commissionHistoryModalApi.setData({ agentId: row.id }).open();
|
||||||
|
}
|
||||||
|
|
||||||
// 刷新处理
|
// 刷新处理
|
||||||
function onRefresh() {
|
function onRefresh() {
|
||||||
gridApi.query();
|
gridApi.query();
|
||||||
@@ -276,10 +337,13 @@ function onRefresh() {
|
|||||||
<FormDrawer @success="onRefresh" />
|
<FormDrawer @success="onRefresh" />
|
||||||
<LinkModalComponent />
|
<LinkModalComponent />
|
||||||
<CommissionModalComponent />
|
<CommissionModalComponent />
|
||||||
|
<CommissionHistoryModalComponent />
|
||||||
<CommissionDeductionModalComponent />
|
<CommissionDeductionModalComponent />
|
||||||
<PlatformDeductionModalComponent />
|
<PlatformDeductionModalComponent />
|
||||||
<RewardModalComponent />
|
<RewardModalComponent />
|
||||||
<WithdrawalModalComponent />
|
<WithdrawalModalComponent />
|
||||||
|
<BalanceModalComponent @success="onRefresh" />
|
||||||
|
<WalletTransactionModalComponent />
|
||||||
|
|
||||||
<!-- 上级代理信息卡片 -->
|
<!-- 上级代理信息卡片 -->
|
||||||
<Card v-if="parentAgentId" class="mb-4">
|
<Card v-if="parentAgentId" class="mb-4">
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
|
import { message, Modal } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
import { getAgentWallet, updateAgentWalletBalance } from '#/api/agent';
|
||||||
|
import type { AgentApi } from '#/api/agent';
|
||||||
|
|
||||||
|
interface ModalData {
|
||||||
|
agentId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 钱包信息
|
||||||
|
const walletInfo = ref<AgentApi.AgentWalletInfo>({
|
||||||
|
balance: 0,
|
||||||
|
frozen_balance: 0,
|
||||||
|
total_earnings: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'success'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const [ModalComponent, modalApi] = useVbenModal({
|
||||||
|
title: '修改余额',
|
||||||
|
destroyOnClose: true,
|
||||||
|
onOpenChange(isOpen) {
|
||||||
|
if (isOpen) {
|
||||||
|
fetchWalletInfo();
|
||||||
|
// 重置表单
|
||||||
|
formApi.setValues({
|
||||||
|
operation_type: 'add',
|
||||||
|
amount: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const modalData = computed(() => modalApi.getData<ModalData>());
|
||||||
|
|
||||||
|
// 获取钱包信息
|
||||||
|
async function fetchWalletInfo() {
|
||||||
|
const agentId = modalData.value?.agentId;
|
||||||
|
if (!agentId) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
const data = await getAgentWallet(agentId);
|
||||||
|
walletInfo.value = data;
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error.message || '获取钱包信息失败');
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单引用
|
||||||
|
const [Form, formApi] = useVbenForm({
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
component: 'RadioGroup',
|
||||||
|
fieldName: 'operation_type',
|
||||||
|
label: '操作类型',
|
||||||
|
componentProps: {
|
||||||
|
options: [
|
||||||
|
{ label: '增加余额', value: 'add' },
|
||||||
|
{ label: '减少余额', value: 'subtract' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
rules: 'required',
|
||||||
|
defaultValue: 'add',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'InputNumber',
|
||||||
|
fieldName: 'amount',
|
||||||
|
label: '金额',
|
||||||
|
componentProps: {
|
||||||
|
precision: 2,
|
||||||
|
min: 0.01,
|
||||||
|
placeholder: '请输入金额',
|
||||||
|
style: { width: '100%' },
|
||||||
|
},
|
||||||
|
rules: 'required',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
showDefaultActions: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 提交前确认
|
||||||
|
async function handleSubmit() {
|
||||||
|
try {
|
||||||
|
const { valid } = await formApi.validate();
|
||||||
|
if (!valid) return;
|
||||||
|
|
||||||
|
const values = await formApi.getValues();
|
||||||
|
if (!values) return;
|
||||||
|
|
||||||
|
// 根据操作类型确定金额正负
|
||||||
|
const finalAmount = values.operation_type === 'subtract' ? -Math.abs(values.amount) : Math.abs(values.amount);
|
||||||
|
|
||||||
|
// 二次确认
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确认修改余额',
|
||||||
|
content: `您确定要${values.operation_type === 'add' ? '增加' : '减少'} ¥${Math.abs(values.amount).toFixed(2)} 的余额吗?`,
|
||||||
|
onOk: async () => {
|
||||||
|
try {
|
||||||
|
await updateAgentWalletBalance({
|
||||||
|
agent_id: modalData.value?.agentId || 0,
|
||||||
|
amount: finalAmount,
|
||||||
|
});
|
||||||
|
|
||||||
|
message.success('修改余额成功');
|
||||||
|
modalApi.close();
|
||||||
|
emit('success');
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error.message || '修改余额失败');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// 表单验证失败,不处理
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
|
function handleReset() {
|
||||||
|
formApi.setValues({
|
||||||
|
operation_type: 'add',
|
||||||
|
amount: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ModalComponent width="500px" :footer="false">
|
||||||
|
<div class="balance-modal">
|
||||||
|
<Form />
|
||||||
|
<div class="form-actions">
|
||||||
|
<a-button @click="handleReset">重置</a-button>
|
||||||
|
<a-button type="primary" @click="handleSubmit">确认修改</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ModalComponent>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.balance-modal {
|
||||||
|
padding: 16px 0;
|
||||||
|
|
||||||
|
.form-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,323 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
import { Card, Statistic, Row, Col, DatePicker, Button, Space } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { getAgentCommissionList } from '#/api/agent';
|
||||||
|
import type { AgentApi } from '#/api';
|
||||||
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
|
|
||||||
|
interface ModalData {
|
||||||
|
agentId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
title: '历史佣金记录',
|
||||||
|
destroyOnClose: true,
|
||||||
|
footer: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const modalData = computed(() => modalApi.getData<ModalData>());
|
||||||
|
|
||||||
|
// 时间范围选择
|
||||||
|
const dateRange = ref<[Dayjs, Dayjs] | undefined>(undefined);
|
||||||
|
|
||||||
|
// 统计数据
|
||||||
|
const statistics = ref({
|
||||||
|
totalAmount: 0,
|
||||||
|
settledAmount: 0,
|
||||||
|
frozenAmount: 0,
|
||||||
|
refundedAmount: 0,
|
||||||
|
totalCount: 0,
|
||||||
|
settledCount: 0,
|
||||||
|
frozenCount: 0,
|
||||||
|
refundedCount: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 快捷时间范围选择
|
||||||
|
function selectTimeRange(range: string) {
|
||||||
|
const now = dayjs();
|
||||||
|
let startDate: Dayjs;
|
||||||
|
|
||||||
|
switch (range) {
|
||||||
|
case '7d':
|
||||||
|
startDate = now.subtract(7, 'day');
|
||||||
|
break;
|
||||||
|
case '1m':
|
||||||
|
startDate = now.subtract(1, 'month');
|
||||||
|
break;
|
||||||
|
case '3m':
|
||||||
|
startDate = now.subtract(3, 'month');
|
||||||
|
break;
|
||||||
|
case '1y':
|
||||||
|
startDate = now.subtract(1, 'year');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dateRange.value = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dateRange.value = [startDate, now];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置时间范围
|
||||||
|
function resetTimeRange() {
|
||||||
|
dateRange.value = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表格配置
|
||||||
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
formOptions: {
|
||||||
|
fieldMappingTime: [['create_time', ['create_time_start', 'create_time_end']]],
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
component: 'InputNumber',
|
||||||
|
fieldName: 'order_id',
|
||||||
|
label: '订单ID',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入订单ID',
|
||||||
|
style: { width: '100%' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
fieldName: 'product_name',
|
||||||
|
label: '产品名称',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入产品名称',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Select',
|
||||||
|
fieldName: 'status',
|
||||||
|
label: '状态',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
placeholder: '请选择状态',
|
||||||
|
options: [
|
||||||
|
{ label: '已结算', value: 0 },
|
||||||
|
{ label: '冻结中', value: 1 },
|
||||||
|
{ label: '已退款', value: 2 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
submitOnChange: true,
|
||||||
|
},
|
||||||
|
gridOptions: {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
field: 'id',
|
||||||
|
title: 'ID',
|
||||||
|
width: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'order_id',
|
||||||
|
title: '订单ID',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'amount',
|
||||||
|
title: '佣金金额',
|
||||||
|
width: 120,
|
||||||
|
formatter: ({ cellValue }: { cellValue: number }) =>
|
||||||
|
`¥${cellValue.toFixed(2)}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'product_name',
|
||||||
|
title: '产品名称',
|
||||||
|
width: 180,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'status',
|
||||||
|
title: '状态',
|
||||||
|
width: 100,
|
||||||
|
formatter: ({ cellValue }: { cellValue: number }) => {
|
||||||
|
const statusMap: Record<number, string> = {
|
||||||
|
0: '已结算',
|
||||||
|
1: '冻结中',
|
||||||
|
2: '已退款',
|
||||||
|
};
|
||||||
|
return statusMap[cellValue] || '未知';
|
||||||
|
},
|
||||||
|
className: ({ cellValue }: { cellValue: number }) => {
|
||||||
|
if (cellValue === 0) return 'text-green-600';
|
||||||
|
if (cellValue === 1) return 'text-orange-600';
|
||||||
|
if (cellValue === 2) return 'text-red-600';
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'create_time',
|
||||||
|
title: '创建时间',
|
||||||
|
width: 160,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
height: 600,
|
||||||
|
maxHeight: 800,
|
||||||
|
keepSource: true,
|
||||||
|
pagerConfig: {
|
||||||
|
enabled: true,
|
||||||
|
pageSize: 20,
|
||||||
|
pageSizes: [10, 20, 50, 100],
|
||||||
|
layouts: ['Total', 'Sizes', 'PrevJump', 'Number', 'NextJump', 'FullJump'],
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
custom: true,
|
||||||
|
export: false,
|
||||||
|
refresh: { code: 'query' },
|
||||||
|
search: true,
|
||||||
|
zoom: true,
|
||||||
|
},
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }: { page: { currentPage: number; pageSize: number } }, formValues: Record<string, any>) => {
|
||||||
|
const params: any = {
|
||||||
|
agent_id: modalData.value?.agentId,
|
||||||
|
...formValues,
|
||||||
|
page: page.currentPage,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加时间范围参数
|
||||||
|
if (dateRange.value && dateRange.value[0] && dateRange.value[1]) {
|
||||||
|
params.create_time_start = dateRange.value[0].format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
params.create_time_end = dateRange.value[1].format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await getAgentCommissionList(params);
|
||||||
|
|
||||||
|
// 更新统计数据
|
||||||
|
updateStatistics(result.items || []);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
result: 'items',
|
||||||
|
total: 'total',
|
||||||
|
},
|
||||||
|
autoLoad: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新统计数据
|
||||||
|
function updateStatistics(items: AgentApi.AgentCommissionListItem[]) {
|
||||||
|
statistics.value = {
|
||||||
|
totalCount: items.length,
|
||||||
|
totalAmount: items.reduce((sum, item) => sum + item.amount, 0),
|
||||||
|
settledCount: items.filter(item => item.status === 0).length,
|
||||||
|
settledAmount: items.filter(item => item.status === 0).reduce((sum, item) => sum + item.amount, 0),
|
||||||
|
frozenCount: items.filter(item => item.status === 1).length,
|
||||||
|
frozenAmount: items.filter(item => item.status === 1).reduce((sum, item) => sum + item.amount, 0),
|
||||||
|
refundedCount: items.filter(item => item.status === 2).length,
|
||||||
|
refundedAmount: items.filter(item => item.status === 2).reduce((sum, item) => sum + item.amount, 0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听时间范围变化,自动刷新表格
|
||||||
|
watch(dateRange, () => {
|
||||||
|
if (gridApi) {
|
||||||
|
gridApi.query();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal class="w-[calc(100vw-200px)]">
|
||||||
|
<div class="commission-history-modal">
|
||||||
|
<!-- 时间范围选择 -->
|
||||||
|
<Card class="mb-4" title="时间范围选择">
|
||||||
|
<Space :size="12">
|
||||||
|
<span class="text-sm text-gray-600">快速选择:</span>
|
||||||
|
<Button size="small" @click="selectTimeRange('7d')">近7日</Button>
|
||||||
|
<Button size="small" @click="selectTimeRange('1m')">近1月</Button>
|
||||||
|
<Button size="small" @click="selectTimeRange('3m')">近3月</Button>
|
||||||
|
<Button size="small" @click="selectTimeRange('1y')">近1年</Button>
|
||||||
|
<Button size="small" type="default" @click="resetTimeRange">全部</Button>
|
||||||
|
<span class="text-sm text-gray-400 ml-4">|</span>
|
||||||
|
<span class="text-sm text-gray-600">自定义:</span>
|
||||||
|
<DatePicker.RangePicker
|
||||||
|
v-model:value="dateRange"
|
||||||
|
show-time
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
:placeholder="['开始时间', '结束时间']"
|
||||||
|
style="width: 380px"
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<!-- 统计卡片 -->
|
||||||
|
<Card class="mb-4" title="统计信息">
|
||||||
|
<Row :gutter="[16, 16]">
|
||||||
|
<Col :span="6">
|
||||||
|
<Statistic title="总记录数" :value="statistics.totalCount" />
|
||||||
|
</Col>
|
||||||
|
<Col :span="6">
|
||||||
|
<Statistic
|
||||||
|
title="总佣金金额"
|
||||||
|
:value="statistics.totalAmount"
|
||||||
|
:precision="2"
|
||||||
|
prefix="¥"
|
||||||
|
:value-style="{ color: '#1890ff' }"
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col :span="6">
|
||||||
|
<Statistic
|
||||||
|
title="已结算金额"
|
||||||
|
:value="statistics.settledAmount"
|
||||||
|
:precision="2"
|
||||||
|
prefix="¥"
|
||||||
|
:value-style="{ color: '#52c41a' }"
|
||||||
|
/>
|
||||||
|
<div class="text-sm text-gray-500 mt-1">
|
||||||
|
{{ statistics.settledCount }} 条记录
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
<Col :span="6">
|
||||||
|
<Statistic
|
||||||
|
title="冻结中金额"
|
||||||
|
:value="statistics.frozenAmount"
|
||||||
|
:precision="2"
|
||||||
|
prefix="¥"
|
||||||
|
:value-style="{ color: '#fa8c16' }"
|
||||||
|
/>
|
||||||
|
<div class="text-sm text-gray-500 mt-1">
|
||||||
|
{{ statistics.frozenCount }} 条记录
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row :gutter="[16, 16]" class="mt-4">
|
||||||
|
<Col :span="12">
|
||||||
|
<Statistic
|
||||||
|
title="已退款金额"
|
||||||
|
:value="statistics.refundedAmount"
|
||||||
|
:precision="2"
|
||||||
|
prefix="¥"
|
||||||
|
:value-style="{ color: '#f5222d' }"
|
||||||
|
/>
|
||||||
|
<div class="text-sm text-gray-500 mt-1">
|
||||||
|
{{ statistics.refundedCount }} 条记录
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<!-- 佣金记录列表 -->
|
||||||
|
<Grid :table-title="`代理商 ID: ${modalData?.agentId} 的历史佣金记录`" />
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.commission-history-modal {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, h } from 'vue';
|
||||||
|
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
import { Tag } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { getWalletTransactionList } from '#/api/agent';
|
||||||
|
|
||||||
|
interface ModalData {
|
||||||
|
agentId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
title: '钱包流水记录',
|
||||||
|
destroyOnClose: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const modalData = computed(() => modalApi.getData<ModalData>());
|
||||||
|
|
||||||
|
// 交易类型映射
|
||||||
|
const transactionTypeMap: Record<string, { label: string; color: string }> = {
|
||||||
|
commission: { label: '佣金收入', color: 'green' },
|
||||||
|
withdraw: { label: '提现', color: 'red' },
|
||||||
|
freeze: { label: '冻结', color: 'orange' },
|
||||||
|
unfreeze: { label: '解冻', color: 'blue' },
|
||||||
|
reward: { label: '奖励', color: 'green' },
|
||||||
|
refund: { label: '退款', color: 'purple' },
|
||||||
|
adjust: { label: '调整', color: 'cyan' },
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取交易类型标签
|
||||||
|
function getTransactionTypeTag(type: string) {
|
||||||
|
const config = transactionTypeMap[type] || { label: type, color: 'default' };
|
||||||
|
return h(Tag, { color: config.color }, config.label);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取金额显示
|
||||||
|
function getAmountCellRender(params: any) {
|
||||||
|
const amount = params.row.amount;
|
||||||
|
const isPositive = amount >= 0;
|
||||||
|
const color = isPositive ? '#52c41a' : '#ff4d4f';
|
||||||
|
const sign = isPositive ? '+' : '';
|
||||||
|
return h('span', { style: { color, fontWeight: 'bold' } }, `${sign}${amount.toFixed(2)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表格列配置
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
field: 'id',
|
||||||
|
title: '流水ID',
|
||||||
|
width: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'transaction_type',
|
||||||
|
title: '交易类型',
|
||||||
|
width: 120,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CustomRender',
|
||||||
|
render: getTransactionTypeTag,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'amount',
|
||||||
|
title: '变动金额',
|
||||||
|
width: 120,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CustomRender',
|
||||||
|
render: getAmountCellRender,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'balance_before',
|
||||||
|
title: '变动前余额',
|
||||||
|
width: 120,
|
||||||
|
formatter: ({ cellValue }: any) => `¥${Number(cellValue || 0).toFixed(2)}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'balance_after',
|
||||||
|
title: '变动后余额',
|
||||||
|
width: 120,
|
||||||
|
formatter: ({ cellValue }: any) => `¥${Number(cellValue || 0).toFixed(2)}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'frozen_balance_before',
|
||||||
|
title: '变动前冻结',
|
||||||
|
width: 120,
|
||||||
|
formatter: ({ cellValue }: any) => `¥${Number(cellValue || 0).toFixed(2)}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'frozen_balance_after',
|
||||||
|
title: '变动后冻结',
|
||||||
|
width: 120,
|
||||||
|
formatter: ({ cellValue }: any) => `¥${Number(cellValue || 0).toFixed(2)}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'transaction_id',
|
||||||
|
title: '关联交易ID',
|
||||||
|
width: 150,
|
||||||
|
formatter: ({ cellValue }: any) => cellValue || '-',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'remark',
|
||||||
|
title: '备注',
|
||||||
|
width: 200,
|
||||||
|
formatter: ({ cellValue }: any) => cellValue || '-',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'create_time',
|
||||||
|
title: '创建时间',
|
||||||
|
width: 180,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const [Grid] = useVbenVxeGrid({
|
||||||
|
formOptions: {
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
fieldName: 'transaction_type',
|
||||||
|
label: '交易类型',
|
||||||
|
help: '如: commission, withdraw, freeze, unfreeze, reward, refund, adjust',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'DatePicker',
|
||||||
|
componentProps: {
|
||||||
|
format: 'YYYY-MM-DD HH:mm:ss',
|
||||||
|
showTime: true,
|
||||||
|
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||||
|
},
|
||||||
|
fieldName: 'create_time_start',
|
||||||
|
label: '开始时间',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'DatePicker',
|
||||||
|
componentProps: {
|
||||||
|
format: 'YYYY-MM-DD HH:mm:ss',
|
||||||
|
showTime: true,
|
||||||
|
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||||
|
},
|
||||||
|
fieldName: 'create_time_end',
|
||||||
|
label: '结束时间',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
submitOnChange: true,
|
||||||
|
},
|
||||||
|
gridOptions: {
|
||||||
|
columns,
|
||||||
|
height: 600,
|
||||||
|
maxHeight: 800,
|
||||||
|
keepSource: true,
|
||||||
|
pagerConfig: {
|
||||||
|
enabled: true,
|
||||||
|
pageSize: 20,
|
||||||
|
pageSizes: [10, 20, 50, 100],
|
||||||
|
layouts: ['Total', 'Sizes', 'PrevJump', 'Number', 'NextJump', 'FullJump'],
|
||||||
|
},
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }: { page: { currentPage: number; pageSize: number } }, formValues: Record<string, any>) => {
|
||||||
|
return await getWalletTransactionList({
|
||||||
|
agent_id: modalData.value?.agentId || 0,
|
||||||
|
...formValues,
|
||||||
|
page: page.currentPage,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
result: 'items',
|
||||||
|
total: 'total',
|
||||||
|
},
|
||||||
|
autoLoad: true,
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
custom: true,
|
||||||
|
export: false,
|
||||||
|
refresh: { code: 'query' },
|
||||||
|
search: true,
|
||||||
|
zoom: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal class="w-[calc(100vw-200px)]" :footer="false">
|
||||||
|
<div class="wallet-transaction-modal">
|
||||||
|
<Grid table-title="钱包流水记录" />
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.wallet-transaction-modal {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
@@ -14,6 +14,18 @@ export function useWithdrawalColumns(): VxeTableGridOptions['columns'] {
|
|||||||
width: 120,
|
width: 120,
|
||||||
formatter: ({ cellValue }) => `¥${cellValue?.toFixed(2) || '0.00'}`,
|
formatter: ({ cellValue }) => `¥${cellValue?.toFixed(2) || '0.00'}`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '扣税金额',
|
||||||
|
field: 'tax_amount',
|
||||||
|
width: 120,
|
||||||
|
formatter: ({ cellValue }) => `¥${cellValue?.toFixed(2) || '0.00'}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '实际转账金额',
|
||||||
|
field: 'actual_amount',
|
||||||
|
width: 120,
|
||||||
|
formatter: ({ cellValue }) => `¥${cellValue?.toFixed(2) || '0.00'}`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '提现类型',
|
title: '提现类型',
|
||||||
field: 'withdraw_type',
|
field: 'withdraw_type',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
import { Page, useVbenDrawer } from '@vben/common-ui';
|
import { Page, useVbenDrawer } from '@vben/common-ui';
|
||||||
import { Button, Space } from 'ant-design-vue';
|
import { Button, Space } from 'ant-design-vue';
|
||||||
@@ -54,19 +54,16 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
submitOnChange: true,
|
submitOnChange: true,
|
||||||
},
|
},
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
columns: useWithdrawalColumns(onActionClick),
|
columns: useWithdrawalColumns(),
|
||||||
proxyConfig: {
|
proxyConfig: {
|
||||||
ajax: {
|
ajax: {
|
||||||
query: async ({
|
query: async ({
|
||||||
page,
|
page,
|
||||||
form,
|
|
||||||
}: {
|
}: {
|
||||||
page: QueryParams;
|
page: QueryParams;
|
||||||
form: Record<string, any>;
|
|
||||||
}) => {
|
}) => {
|
||||||
return await getAgentWithdrawalList({
|
return await getAgentWithdrawalList({
|
||||||
...queryParams.value,
|
...queryParams.value,
|
||||||
...form,
|
|
||||||
page: page.currentPage,
|
page: page.currentPage,
|
||||||
pageSize: page.pageSize,
|
pageSize: page.pageSize,
|
||||||
});
|
});
|
||||||
@@ -95,7 +92,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
<template #operation="{ row }">
|
<template #operation="{ row }">
|
||||||
<Space>
|
<Space>
|
||||||
<Button
|
<Button
|
||||||
v-if="row.withdraw_type === 2 && row.status === 1"
|
v-if="row.status === 1"
|
||||||
type="link"
|
type="link"
|
||||||
@click="onActionClick({ code: 'review', row })"
|
@click="onActionClick({ code: 'review', row })"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { message } from 'ant-design-vue';
|
|||||||
|
|
||||||
import { useVbenForm } from '#/adapter/form';
|
import { useVbenForm } from '#/adapter/form';
|
||||||
import { reviewBankCardWithdrawal } from '#/api/agent';
|
import { reviewBankCardWithdrawal } from '#/api/agent';
|
||||||
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'success'): void;
|
(e: 'success'): void;
|
||||||
@@ -14,8 +15,8 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
const formData = ref<any>(null);
|
const formData = ref<any>(null);
|
||||||
|
|
||||||
const [Form, formApi] = useVbenForm({
|
// 表单配置(包含所有字段)
|
||||||
schema: [
|
const getFormSchema = (): VbenFormSchema[] => [
|
||||||
{
|
{
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
fieldName: 'withdraw_no',
|
fieldName: 'withdraw_no',
|
||||||
@@ -24,6 +25,14 @@ const [Form, formApi] = useVbenForm({
|
|||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
fieldName: 'withdraw_type',
|
||||||
|
label: '提现类型',
|
||||||
|
componentProps: {
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
component: 'InputNumber',
|
component: 'InputNumber',
|
||||||
fieldName: 'amount',
|
fieldName: 'amount',
|
||||||
@@ -36,8 +45,9 @@ const [Form, formApi] = useVbenForm({
|
|||||||
addonBefore: '¥',
|
addonBefore: '¥',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// 银行卡和支付宝通用字段:扣税金额
|
||||||
{
|
{
|
||||||
component: 'InputNumber',
|
component: 'InputNumber' ,
|
||||||
fieldName: 'tax_amount',
|
fieldName: 'tax_amount',
|
||||||
label: '扣税金额',
|
label: '扣税金额',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
@@ -48,6 +58,7 @@ const [Form, formApi] = useVbenForm({
|
|||||||
addonBefore: '¥',
|
addonBefore: '¥',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// 银行卡和支付宝通用字段:实际转账金额
|
||||||
{
|
{
|
||||||
component: 'InputNumber',
|
component: 'InputNumber',
|
||||||
fieldName: 'actual_amount',
|
fieldName: 'actual_amount',
|
||||||
@@ -60,14 +71,16 @@ const [Form, formApi] = useVbenForm({
|
|||||||
addonBefore: '¥',
|
addonBefore: '¥',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// 银行卡和支付宝都有:收款账号
|
||||||
{
|
{
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
fieldName: 'bank_card_no',
|
fieldName: 'payee_account',
|
||||||
label: '银行卡号',
|
label: '收款账号',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// 银行卡提现特有字段:开户支行
|
||||||
{
|
{
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
fieldName: 'bank_name',
|
fieldName: 'bank_name',
|
||||||
@@ -75,7 +88,12 @@ const [Form, formApi] = useVbenForm({
|
|||||||
componentProps: {
|
componentProps: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
|
dependencies: {
|
||||||
|
show: (values) => values.withdraw_type === '银行卡',
|
||||||
|
triggerFields: ['withdraw_type'],
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
// 银行卡提现特有字段:收款人姓名
|
||||||
{
|
{
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
fieldName: 'payee_name',
|
fieldName: 'payee_name',
|
||||||
@@ -83,6 +101,10 @@ const [Form, formApi] = useVbenForm({
|
|||||||
componentProps: {
|
componentProps: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
|
dependencies: {
|
||||||
|
show: (values) => values.withdraw_type === '银行卡',
|
||||||
|
triggerFields: ['withdraw_type'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'RadioGroup',
|
component: 'RadioGroup',
|
||||||
@@ -109,11 +131,16 @@ const [Form, formApi] = useVbenForm({
|
|||||||
style: { width: '100%' },
|
style: { width: '100%' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
];
|
||||||
|
|
||||||
|
const [Form, formApi] = useVbenForm({
|
||||||
|
schema: getFormSchema(),
|
||||||
showDefaultActions: false,
|
showDefaultActions: false,
|
||||||
|
wrapperClass: 'agent-withdrawal-form-wrapper',
|
||||||
});
|
});
|
||||||
|
|
||||||
const [Drawer, drawerApi] = useVbenDrawer({
|
const [Drawer, drawerApi] = useVbenDrawer({
|
||||||
|
class: 'agent-withdrawal-review-drawer',
|
||||||
async onConfirm() {
|
async onConfirm() {
|
||||||
const { valid } = await formApi.validate();
|
const { valid } = await formApi.validate();
|
||||||
if (!valid) return;
|
if (!valid) return;
|
||||||
@@ -153,30 +180,70 @@ const [Drawer, drawerApi] = useVbenDrawer({
|
|||||||
formApi.resetForm();
|
formApi.resetForm();
|
||||||
if (data) {
|
if (data) {
|
||||||
formData.value = data;
|
formData.value = data;
|
||||||
// 设置表单初始值
|
|
||||||
formApi.setValues({
|
// 根据提现类型设置不同的表单初始值
|
||||||
|
const typeMap: Record<number, string> = {
|
||||||
|
1: '支付宝',
|
||||||
|
2: '银行卡',
|
||||||
|
};
|
||||||
|
const initialValues: any = {
|
||||||
withdraw_no: data.withdraw_no || '',
|
withdraw_no: data.withdraw_no || '',
|
||||||
|
withdraw_type: typeMap[data.withdraw_type] || '未知',
|
||||||
amount: data.amount || 0,
|
amount: data.amount || 0,
|
||||||
tax_amount: data.tax_amount || 0,
|
|
||||||
actual_amount: data.actual_amount || 0,
|
|
||||||
bank_card_no: data.bank_card_no || '',
|
|
||||||
bank_name: data.bank_name || '',
|
|
||||||
payee_name: data.payee_name || '',
|
|
||||||
action: 1, // 默认选择确认
|
action: 1, // 默认选择确认
|
||||||
remark: '',
|
remark: '',
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// 银行卡提现特有字段
|
||||||
|
if (data.withdraw_type === 2) {
|
||||||
|
initialValues.tax_amount = data.tax_amount || 0;
|
||||||
|
initialValues.actual_amount = data.actual_amount || 0;
|
||||||
|
initialValues.payee_account = data.bank_card_no || '';
|
||||||
|
initialValues.bank_name = data.bank_name || '';
|
||||||
|
initialValues.payee_name = data.payee_name || '';
|
||||||
|
} else {
|
||||||
|
// 支付宝提现
|
||||||
|
initialValues.tax_amount = data.tax_amount || 0;
|
||||||
|
initialValues.actual_amount = data.actual_amount || 0;
|
||||||
|
initialValues.payee_account = data.payee_account || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
formApi.setValues(initialValues);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const getDrawerTitle = computed(() => {
|
const getDrawerTitle = computed(() => {
|
||||||
return `银行卡提现审核 ${formData.value?.withdraw_no || ''}`;
|
const typeMap: Record<number, string> = {
|
||||||
|
1: '支付宝',
|
||||||
|
2: '银行卡',
|
||||||
|
};
|
||||||
|
const typeName = typeMap[formData.value?.withdraw_type] || '未知';
|
||||||
|
return `${typeName}提现审核 ${formData.value?.withdraw_no || ''}`;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Drawer :title="getDrawerTitle" width="600">
|
<Drawer :title="getDrawerTitle" :width="800">
|
||||||
|
<div class="agent-withdrawal-review-content">
|
||||||
<Form />
|
<Form />
|
||||||
|
</div>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.agent-withdrawal-review-content {
|
||||||
|
padding: 0 4px;
|
||||||
|
max-height: calc(100vh - 120px);
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-withdrawal-review-content :deep(.ant-form-item) {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-withdrawal-review-content :deep(.ant-form-item-label) {
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -4,16 +4,31 @@ import type { EchartsUIType } from '@vben/plugins/echarts';
|
|||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||||
|
import { getOrderSourceStatistics } from '#/api/order/order';
|
||||||
|
|
||||||
const chartRef = ref<EchartsUIType>();
|
const chartRef = ref<EchartsUIType>();
|
||||||
const { renderEcharts } = useEcharts(chartRef);
|
const { renderEcharts } = useEcharts(chartRef);
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
onMounted(() => {
|
// 获取订单来源统计数据
|
||||||
|
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({
|
renderEcharts({
|
||||||
legend: {
|
legend: {
|
||||||
bottom: '2%',
|
bottom: '2%',
|
||||||
left: 'center',
|
left: 'center',
|
||||||
data: ['产品A', '产品B', '产品C', '产品D'],
|
data: data.map(item => item.name),
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
@@ -23,13 +38,13 @@ onMounted(() => {
|
|||||||
animationEasing: 'exponentialInOut',
|
animationEasing: 'exponentialInOut',
|
||||||
animationType: 'scale',
|
animationType: 'scale',
|
||||||
avoidLabelOverlap: false,
|
avoidLabelOverlap: false,
|
||||||
color: ['#5ab1ef', '#b6a2de', '#67e0e3', '#2ec7c9'],
|
color: [
|
||||||
data: [
|
'#5ab1ef', '#b6a2de', '#67e0e3', '#2ec7c9',
|
||||||
{ name: '产品A', value: 1048 },
|
'#ffb980', '#d87a80', '#8d98b3', '#e5cf0d',
|
||||||
{ name: '产品B', value: 735 },
|
'#97b552', '#95706d', '#dc69aa', '#07a2a4',
|
||||||
{ name: '产品C', value: 580 },
|
'#9a7fd1', '#588dd5', '#f5994e', '#c05050'
|
||||||
{ name: '产品D', value: 484 },
|
|
||||||
],
|
],
|
||||||
|
data: data,
|
||||||
emphasis: {
|
emphasis: {
|
||||||
label: {
|
label: {
|
||||||
fontSize: '12',
|
fontSize: '12',
|
||||||
@@ -38,7 +53,6 @@ onMounted(() => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
// borderColor: '#fff',
|
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
},
|
},
|
||||||
@@ -56,11 +70,25 @@ onMounted(() => {
|
|||||||
],
|
],
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
|
formatter: '{b}: {c} ({d}%)',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取订单来源统计数据失败:', error);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchOrderSourceStatistics();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<EchartsUI ref="chartRef" />
|
<div v-if="loading" class="flex justify-center items-center h-64">
|
||||||
|
加载中...
|
||||||
|
</div>
|
||||||
|
<EchartsUI v-else ref="chartRef" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,59 +1,59 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { EchartsUIType } from '@vben/plugins/echarts';
|
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 { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||||
|
import { Button } from 'ant-design-vue';
|
||||||
import { getOrderList } from '#/api/order/order';
|
import { getOrderStatistics } from '#/api/order/order-statistics';
|
||||||
|
|
||||||
const chartRef = ref<EchartsUIType>();
|
const chartRef = ref<EchartsUIType>();
|
||||||
const { renderEcharts } = useEcharts(chartRef);
|
const { renderEcharts } = useEcharts(chartRef);
|
||||||
|
|
||||||
|
// 时间维度状态
|
||||||
|
const timeDimension = ref<'day' | 'month' | 'year' | 'all'>('day');
|
||||||
|
|
||||||
onMounted(async () => {
|
// 获取订单统计数据
|
||||||
|
async function fetchOrderStatistics() {
|
||||||
try {
|
try {
|
||||||
// 准备图表数据
|
console.log('Fetching order statistics with dimension:', timeDimension.value);
|
||||||
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' });
|
|
||||||
});
|
|
||||||
|
|
||||||
// 准备日期数组,用于查询订单数据
|
// 使用后端API获取数据
|
||||||
const queryDates = Array.from({ length: 30 }).map((_, index) => {
|
const response = await getOrderStatistics(timeDimension.value);
|
||||||
const date = new Date();
|
console.log('Order statistics response:', response);
|
||||||
date.setDate(date.getDate() - 29 + index);
|
|
||||||
return date.toISOString().split('T')[0]; // YYYY-MM-DD格式
|
|
||||||
});
|
|
||||||
|
|
||||||
// 获取每日订单数据
|
let items = response.items || [];
|
||||||
const orderDataPromises = queryDates.map(async (date) => {
|
|
||||||
try {
|
|
||||||
// 获取当天的开始和结束时间
|
|
||||||
const startTime = `${date} 00:00:00`;
|
|
||||||
const endTime = `${date} 23:59:59`;
|
|
||||||
|
|
||||||
// 获取当天的订单数据
|
// 如果后端返回空数据,显示空状态
|
||||||
const response = await getOrderList({
|
if (items.length === 0) {
|
||||||
page: 1,
|
console.log('No data from backend, showing empty chart');
|
||||||
pageSize: 1,
|
items = [];
|
||||||
create_time_start: startTime,
|
}
|
||||||
create_time_end: endTime
|
|
||||||
});
|
|
||||||
|
|
||||||
return response.total || 0;
|
console.log('Items for chart:', items);
|
||||||
} catch (error) {
|
|
||||||
console.error(`获取${date}的订单数据失败:`, error);
|
// 按日期排序
|
||||||
return 0;
|
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 = items.map((item: any) => item.count);
|
||||||
const orderData = await Promise.all(orderDataPromises);
|
const amountData = items.map((item: any) => item.amount);
|
||||||
|
|
||||||
// 计算Y轴最大值
|
// 计算Y轴最大值
|
||||||
const maxValue = Math.max(...orderData) || 10;
|
const maxOrderValue = Math.max(...orderData) || 10;
|
||||||
|
const maxAmountValue = Math.max(...amountData) || 10;
|
||||||
|
|
||||||
renderEcharts({
|
renderEcharts({
|
||||||
grid: {
|
grid: {
|
||||||
@@ -61,7 +61,11 @@ onMounted(async () => {
|
|||||||
containLabel: true,
|
containLabel: true,
|
||||||
left: '1%',
|
left: '1%',
|
||||||
right: '1%',
|
right: '1%',
|
||||||
top: '2%',
|
top: '10%',
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['订单数', '订单金额'],
|
||||||
|
top: 0,
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
@@ -77,6 +81,19 @@ onMounted(async () => {
|
|||||||
color: '#4f9cff',
|
color: '#4f9cff',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
data: amountData,
|
||||||
|
type: 'line',
|
||||||
|
name: '订单金额',
|
||||||
|
smooth: true,
|
||||||
|
itemStyle: {
|
||||||
|
color: '#52c41a',
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.3,
|
||||||
|
color: '#52c41a',
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
tooltip: {
|
tooltip: {
|
||||||
axisPointer: {
|
axisPointer: {
|
||||||
@@ -86,8 +103,15 @@ onMounted(async () => {
|
|||||||
},
|
},
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
formatter: (params: any) => {
|
formatter: (params: any) => {
|
||||||
const param = params[0];
|
let result = `${params[0].axisValue}<br/>`;
|
||||||
return `${param.axisValue}<br/>${param.seriesName}: ${param.value}`;
|
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: {
|
xAxis: {
|
||||||
@@ -95,11 +119,22 @@ onMounted(async () => {
|
|||||||
type: 'category',
|
type: 'category',
|
||||||
boundaryGap: false,
|
boundaryGap: false,
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: [
|
||||||
max: Math.ceil(maxValue * 1.2), // 比最大值大20%作为Y轴上限
|
{
|
||||||
splitNumber: 4,
|
|
||||||
type: 'value',
|
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) {
|
} catch (error) {
|
||||||
console.error('获取订单趋势数据失败:', error);
|
console.error('获取订单趋势数据失败:', error);
|
||||||
@@ -111,7 +146,11 @@ onMounted(async () => {
|
|||||||
containLabel: true,
|
containLabel: true,
|
||||||
left: '1%',
|
left: '1%',
|
||||||
right: '1%',
|
right: '1%',
|
||||||
top: '2%',
|
top: '10%',
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['订单数', '订单金额'],
|
||||||
|
top: 0,
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
@@ -127,6 +166,19 @@ onMounted(async () => {
|
|||||||
color: '#4f9cff',
|
color: '#4f9cff',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
data: Array(30).fill(0),
|
||||||
|
type: 'line',
|
||||||
|
name: '订单金额',
|
||||||
|
smooth: true,
|
||||||
|
itemStyle: {
|
||||||
|
color: '#52c41a',
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.3,
|
||||||
|
color: '#52c41a',
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
tooltip: {
|
tooltip: {
|
||||||
axisPointer: {
|
axisPointer: {
|
||||||
@@ -145,16 +197,66 @@ onMounted(async () => {
|
|||||||
type: 'category',
|
type: 'category',
|
||||||
boundaryGap: false,
|
boundaryGap: false,
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: [
|
||||||
|
{
|
||||||
|
type: 'value',
|
||||||
|
name: '订单数',
|
||||||
|
position: 'left',
|
||||||
max: 10,
|
max: 10,
|
||||||
splitNumber: 4,
|
splitNumber: 4,
|
||||||
type: 'value',
|
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'value',
|
||||||
|
name: '金额(¥)',
|
||||||
|
position: 'right',
|
||||||
|
max: 10,
|
||||||
|
splitNumber: 4,
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 组件挂载时获取数据
|
||||||
|
onMounted(() => {
|
||||||
|
fetchOrderStatistics();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听时间维度变化
|
||||||
|
watch(timeDimension, () => {
|
||||||
|
fetchOrderStatistics();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<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" />
|
<EchartsUI ref="chartRef" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -22,7 +22,7 @@ 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, getWithdrawalStatistics } from '#/api/agent';
|
import { getAgentStatistics, getWithdrawalStatistics, getAgentOrderStatistics } from '#/api/agent';
|
||||||
import { getOrderList, getRefundStatistics, getIncomeStatistics } from '#/api/order/order';
|
import { getOrderList, getRefundStatistics, getIncomeStatistics } from '#/api/order/order';
|
||||||
import { getPlatformUserList } from '#/api/platform-user';
|
import { getPlatformUserList } from '#/api/platform-user';
|
||||||
|
|
||||||
@@ -52,9 +52,9 @@ const overviewItems = ref<AnalysisOverviewItem[]>([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: SvgDownloadIcon,
|
icon: SvgDownloadIcon,
|
||||||
title: '总收入',
|
title: '总收入流水金额',
|
||||||
value: 0,
|
value: 0,
|
||||||
todaytitle: '今日新增收入',
|
todaytitle: '今日新增收入流水金额',
|
||||||
todayValue: 0,
|
todayValue: 0,
|
||||||
Subtitle: '总利润',
|
Subtitle: '总利润',
|
||||||
SubValue: 0,
|
SubValue: 0,
|
||||||
@@ -65,24 +65,28 @@ const overviewItems = ref<AnalysisOverviewItem[]>([
|
|||||||
icon: SvgBellIcon,
|
icon: SvgBellIcon,
|
||||||
title: '总提现金额',
|
title: '总提现金额',
|
||||||
value: 0,
|
value: 0,
|
||||||
|
SubValue: 0,
|
||||||
|
todaySubtitle: '总实际到账金额',
|
||||||
|
todaySubValue: 0,
|
||||||
|
extraTitle: '总扣税金额',
|
||||||
|
extraValue: 0,
|
||||||
todaytitle: '今日新增提现金额',
|
todaytitle: '今日新增提现金额',
|
||||||
todayValue: 0,
|
todayValue: 0,
|
||||||
Subtitle: '总退款金额',
|
Subtitle: '总退款金额',
|
||||||
SubValue: 0,
|
extra2Title: '今日新增退款金额',
|
||||||
todaySubtitle: '今日新增退款金额',
|
extra2Value: 0,
|
||||||
todaySubValue: 0,
|
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const chartTabs: TabOption[] = [
|
const chartTabs: TabOption[] = [
|
||||||
{
|
|
||||||
label: '推广访问趋势',
|
|
||||||
value: 'trends',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: '订单趋势',
|
label: '订单趋势',
|
||||||
value: 'visits',
|
value: 'visits',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '推广访问趋势',
|
||||||
|
value: 'trends',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// 获取统计数据
|
// 获取统计数据
|
||||||
@@ -111,8 +115,8 @@ async function fetchStatistics() {
|
|||||||
const orderTotal = orderResponse.total || 0;
|
const orderTotal = orderResponse.total || 0;
|
||||||
|
|
||||||
// 获取代理订单数据
|
// 获取代理订单数据
|
||||||
const agentOrderResponse = await getOrderList({ page: 1, pageSize: 1, is_agent_order: true });
|
const agentOrderResponse = await getAgentOrderStatistics();
|
||||||
const agentOrderTotal = agentOrderResponse.total || 0;
|
const agentOrderTotal = agentOrderResponse.total_agent_order_count || 0;
|
||||||
|
|
||||||
// 获取今日新增订单数
|
// 获取今日新增订单数
|
||||||
const todayOrderResponse = await getOrderList({
|
const todayOrderResponse = await getOrderList({
|
||||||
@@ -124,34 +128,22 @@ async function fetchStatistics() {
|
|||||||
const todayOrderTotal = todayOrderResponse.total || 0;
|
const todayOrderTotal = todayOrderResponse.total || 0;
|
||||||
|
|
||||||
// 获取今日新增代理订单数
|
// 获取今日新增代理订单数
|
||||||
const todayAgentOrderResponse = await getOrderList({
|
const todayAgentOrderResponse = await getAgentOrderStatistics();
|
||||||
page: 1,
|
const todayAgentOrderTotal = todayAgentOrderResponse.today_agent_order_count || 0;
|
||||||
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
|
// Product data is no longer needed for order statistics
|
||||||
|
|
||||||
// 获取代理数据(总数)
|
// 获取代理统计数据
|
||||||
const agentResponse = await getAgentList({ page: 1, pageSize: 1 });
|
const agentStatsResponse = await getAgentStatistics();
|
||||||
const agentTotal = agentResponse.total || 0;
|
const agentTotal = agentStatsResponse.total_agent_count || 0;
|
||||||
|
const newAgentCount = agentStatsResponse.today_agent_count || 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 withdrawalStatsResponse = await getWithdrawalStatistics();
|
||||||
const totalWithdrawalAmount = withdrawalStatsResponse.total_withdrawal_amount || 0;
|
const totalWithdrawalAmount = withdrawalStatsResponse.total_withdrawal_amount || 0;
|
||||||
const todayWithdrawalAmount = withdrawalStatsResponse.today_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();
|
const refundStatsResponse = await getRefundStatistics();
|
||||||
@@ -191,9 +183,9 @@ async function fetchStatistics() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: SvgDownloadIcon,
|
icon: SvgDownloadIcon,
|
||||||
title: '总收入',
|
title: '总收入流水金额',
|
||||||
value: totalIncome,
|
value: totalIncome,
|
||||||
todaytitle: '今日新增收入',
|
todaytitle: '今日新增收入流水金额',
|
||||||
todayValue: todayIncome,
|
todayValue: todayIncome,
|
||||||
Subtitle: '总利润',
|
Subtitle: '总利润',
|
||||||
SubValue: totalProfit,
|
SubValue: totalProfit,
|
||||||
@@ -206,10 +198,14 @@ async function fetchStatistics() {
|
|||||||
value: totalWithdrawalAmount,
|
value: totalWithdrawalAmount,
|
||||||
todaytitle: '今日新增提现金额',
|
todaytitle: '今日新增提现金额',
|
||||||
todayValue: todayWithdrawalAmount,
|
todayValue: todayWithdrawalAmount,
|
||||||
|
extra2Title: '今日新增退款金额',
|
||||||
|
extra2Value: todayRefundAmount,
|
||||||
Subtitle: '总退款金额',
|
Subtitle: '总退款金额',
|
||||||
SubValue: totalRefundAmount,
|
SubValue: totalRefundAmount,
|
||||||
todaySubtitle: '今日新增退款金额',
|
todaySubtitle: '总实际到账金额',
|
||||||
todaySubValue: todayRefundAmount,
|
todaySubValue: totalActualAmount,
|
||||||
|
extraTitle: '总扣税金额',
|
||||||
|
extraValue: totalTaxAmount,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -227,12 +223,12 @@ onMounted(() => {
|
|||||||
<div class="p-5">
|
<div class="p-5">
|
||||||
<AnalysisOverview :items="overviewItems" />
|
<AnalysisOverview :items="overviewItems" />
|
||||||
<AnalysisChartsTabs :tabs="chartTabs" class="mt-5">
|
<AnalysisChartsTabs :tabs="chartTabs" class="mt-5">
|
||||||
<template #trends>
|
|
||||||
<AnalyticsTrends />
|
|
||||||
</template>
|
|
||||||
<template #visits>
|
<template #visits>
|
||||||
<AnalyticsVisits />
|
<AnalyticsVisits />
|
||||||
</template>
|
</template>
|
||||||
|
<template #trends>
|
||||||
|
<AnalyticsTrends />
|
||||||
|
</template>
|
||||||
</AnalysisChartsTabs>
|
</AnalysisChartsTabs>
|
||||||
|
|
||||||
<div class="mt-5 w-full md:flex">
|
<div class="mt-5 w-full md:flex">
|
||||||
|
|||||||
@@ -173,6 +173,21 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
fieldName: 'platform_order_id',
|
fieldName: 'platform_order_id',
|
||||||
label: '支付订单号',
|
label: '支付订单号',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
fieldName: 'query_name',
|
||||||
|
label: '被查询人姓名',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
fieldName: 'query_id_card',
|
||||||
|
label: '被查询人身份证',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
fieldName: 'query_mobile',
|
||||||
|
label: '被查询人手机号',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
fieldName: 'product_name',
|
fieldName: 'product_name',
|
||||||
|
|||||||
@@ -36,6 +36,18 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
},
|
},
|
||||||
defaultValue: 0,
|
defaultValue: 0,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: 'Switch',
|
||||||
|
fieldName: 'disable',
|
||||||
|
label: '是否封禁',
|
||||||
|
componentProps: {
|
||||||
|
checkedChildren: '已封禁',
|
||||||
|
unCheckedChildren: '正常',
|
||||||
|
checkedValue: 1,
|
||||||
|
unCheckedValue: 0,
|
||||||
|
},
|
||||||
|
defaultValue: 0,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,6 +76,18 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: 'Select',
|
||||||
|
fieldName: 'disable',
|
||||||
|
label: '封禁状态',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
options: [
|
||||||
|
{ label: '正常', value: 0 },
|
||||||
|
{ label: '已封禁', value: 1 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
component: 'RangePicker',
|
component: 'RangePicker',
|
||||||
fieldName: 'create_time',
|
fieldName: 'create_time',
|
||||||
@@ -104,6 +128,12 @@ export function useColumns<T = PlatformUserApi.PlatformUserItem>(
|
|||||||
width: 100,
|
width: 100,
|
||||||
formatter: ({ cellValue }) => (cellValue === 1 ? '是' : '否'),
|
formatter: ({ cellValue }) => (cellValue === 1 ? '是' : '否'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: 'disable',
|
||||||
|
title: '封禁状态',
|
||||||
|
width: 100,
|
||||||
|
formatter: ({ cellValue }) => (cellValue === 1 ? '已封禁' : '正常'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
field: 'create_time',
|
field: 'create_time',
|
||||||
title: '创建时间',
|
title: '创建时间',
|
||||||
|
|||||||
@@ -55,6 +55,18 @@ withDefaults(defineProps<Props>(), {
|
|||||||
+<VbenCountToAnimator :end-val="item.todaySubValue" />
|
+<VbenCountToAnimator :end-val="item.todaySubValue" />
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
|
<p v-if="item.extraTitle" class="text-xs text-muted-foreground flex justify-between mt-1">
|
||||||
|
<span>{{ item.extraTitle }}</span>
|
||||||
|
<span class="font-medium text-foreground">
|
||||||
|
<VbenCountToAnimator :end-val="item.extraValue || 0" />
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p v-if="item.extra2Title" class="text-xs text-muted-foreground flex justify-between mt-1">
|
||||||
|
<span>{{ item.extra2Title }}</span>
|
||||||
|
<span class="font-medium text-foreground">
|
||||||
|
+<VbenCountToAnimator :end-val="item.extra2Value || 0" />
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ interface AnalysisOverviewItem {
|
|||||||
SubValue: number;
|
SubValue: number;
|
||||||
todaySubtitle: string;
|
todaySubtitle: string;
|
||||||
todaySubValue: number;
|
todaySubValue: number;
|
||||||
|
extraTitle?: string;
|
||||||
|
extraValue?: number;
|
||||||
|
extra2Title?: string;
|
||||||
|
extra2Value?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WorkbenchProjectItem {
|
interface WorkbenchProjectItem {
|
||||||
|
|||||||
Reference in New Issue
Block a user