c
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Deploy Website on push / Rerun on failure (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Issue Close Require / close-issues (push) Has been cancelled
Close stale issues / stale (push) Has been cancelled

This commit is contained in:
2025-12-26 22:34:56 +08:00
parent 7743cdc29a
commit 8d179aa10c
8 changed files with 347 additions and 65 deletions

View File

@@ -1,6 +0,0 @@
echo Start running commit-msg hook...
# Check whether the git commit information is standardized
pnpm exec commitlint --edit "$1"
echo Run commit-msg hook done.

View File

@@ -1,7 +0,0 @@
# update `.vscode/vben-admin.code-workspace` file
pnpm vsh code-workspace --auto-commit
# Format and submit code according to lintstagedrc.js configuration
pnpm exec lint-staged
echo Run pre-commit hook done.

View File

@@ -111,10 +111,16 @@ export namespace AgentApi {
agent_id: number;
withdraw_no: string;
amount: number;
actual_amount: number; // 实际到账金额(扣税后)
tax_amount: number; // 扣税金额
status: number;
payee_account: string;
remark: string;
create_time: string;
withdraw_type: number; // 提现类型:1-支付宝,2-银行卡
bank_card_no?: string; // 银行卡号
bank_name?: string; // 开户支行
payee_name?: string; // 收款人姓名
}
export interface AgentWithdrawalList {
@@ -128,6 +134,8 @@ export namespace AgentApi {
agent_id?: number;
status?: number;
withdraw_no?: string;
status?: number;
withdraw_no?: string;
}
// 代理上级抽佣相关接口
@@ -446,6 +454,24 @@ async function updateAgentMembershipConfig(
);
}
/**
* 银行卡提现审核
*/
export interface ReviewBankCardWithdrawalParams {
withdrawal_id: number;
action: 1 | 2; // 1-确认, 2-拒绝
remark?: string;
}
async function reviewBankCardWithdrawal(
params: ReviewBankCardWithdrawalParams,
) {
return requestClient.post<{ success: boolean }>(
'/agent/agent-withdrawal/bank-card/review',
params,
);
}
export {
getAgentCommissionDeductionList,
getAgentCommissionList,
@@ -457,6 +483,7 @@ export {
getAgentRewardList,
getAgentWithdrawalList,
getMembershipRechargeOrderList,
reviewBankCardWithdrawal,
updateAgentMembershipConfig,
updateAgentProductionConfig,
};

View File

@@ -15,7 +15,7 @@ export const overridesPreferences = defineOverridesPreferences({
source: 'https://ctrlph.tianyuandb.com/logo.png',
},
copyright: {
companyName: '海南天远大数据科技有限公司',
companyName: '海南海宇大数据有限公司',
companySiteLink: 'https://www.tianyuandb.com',
date: '2025',
icp: '琼ICP备2024048057号-1',

View File

@@ -1,9 +1,6 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
type WithdrawalMethod = 'alipay' | 'bank' | 'wechat';
type WithdrawalStatus = 'approved' | 'failed' | 'paid' | 'pending' | 'rejected';
export function useWithdrawalColumns(): VxeTableGridOptions['columns'] {
return [
{
@@ -15,39 +12,88 @@ export function useWithdrawalColumns(): VxeTableGridOptions['columns'] {
title: '提现金额',
field: 'amount',
width: 120,
formatter: ({ cellValue }) => `¥${cellValue.toFixed(2)}`,
formatter: ({ cellValue }) => `¥${cellValue?.toFixed(2) || '0.00'}`,
},
{
title: '提现方式',
field: 'method',
width: 120,
formatter: ({ cellValue }: { cellValue: WithdrawalMethod }) => {
const methodMap: Record<WithdrawalMethod, string> = {
alipay: '支付宝',
wechat: '微信',
bank: '银行卡',
title: '提现类型',
field: 'withdraw_type',
width: 100,
formatter: ({ cellValue }) => {
const typeMap: Record<number, string> = {
1: '支付宝',
2: '银行卡',
};
return methodMap[cellValue] || cellValue;
return typeMap[cellValue] || '未知';
},
},
{
title: '收款账号',
field: 'account',
width: 180,
title: '收款信息',
field: 'payee_info',
width: 200,
formatter: ({ row }) => {
if (row.withdraw_type === 1) {
// 支付宝提现
return row.payee_account || '-';
} else if (row.withdraw_type === 2) {
// 银行卡提现
if (row.bank_card_no) {
const cardNo = row.bank_card_no.replaceAll(/\s/g, '');
const masked =
cardNo.length > 8
? `${cardNo.slice(0, 4)} **** **** ${cardNo.slice(
Math.max(0, cardNo.length - 4),
)}`
: cardNo;
return `${masked}${row.bank_name ? ` (${row.bank_name})` : ''}`;
}
return '-';
}
return '-';
},
},
{
title: '收款人',
field: 'payee_name',
width: 120,
formatter: ({ cellValue, row }) => {
if (row.withdraw_type === 2 && cellValue) {
return cellValue;
}
return '-';
},
},
{
title: '状态',
field: 'status',
width: 100,
formatter: ({ cellValue }: { cellValue: WithdrawalStatus }) => {
const statusMap: Record<WithdrawalStatus, string> = {
pending: '待审核',
approved: '已通过',
rejected: '已拒绝',
paid: '已打款',
failed: '打款失败',
formatter: ({ cellValue }) => {
const statusMap: Record<number, string> = {
1: '申请中',
2: '成功',
3: '失败',
};
return statusMap[cellValue] || cellValue;
return statusMap[cellValue] || '未知';
},
cellRender: {
name: 'VxeTag',
props: ({ row }) => {
const statusConfig: Record<
number,
{ content: string; type: string }
> = {
1: { type: 'warning', content: '申请中' },
2: { type: 'success', content: '成功' },
3: { type: 'error', content: '失败' },
};
const config = statusConfig[row.status] || {
type: 'info',
content: '未知',
};
return {
type: config.type,
content: config.content,
};
},
},
},
{
@@ -55,20 +101,19 @@ export function useWithdrawalColumns(): VxeTableGridOptions['columns'] {
field: 'create_time',
width: 180,
},
{
title: '审核时间',
field: 'audit_time',
width: 180,
},
{
title: '打款时间',
field: 'pay_time',
width: 180,
},
{
title: '备注',
field: 'remark',
width: 200,
formatter: ({ cellValue }) => cellValue || '-',
},
{
align: 'center',
slots: { default: 'operation' },
field: 'operation',
fixed: 'right',
title: '操作',
width: 120,
},
];
}
@@ -76,14 +121,13 @@ export function useWithdrawalColumns(): VxeTableGridOptions['columns'] {
export function useWithdrawalFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'method',
label: '提现方式',
fieldName: 'withdraw_type',
label: '提现类型',
component: 'Select',
componentProps: {
options: [
{ label: '支付宝', value: 'alipay' },
{ label: '微信', value: 'wechat' },
{ label: '银行卡', value: 'bank' },
{ label: '支付宝', value: 1 },
{ label: '银行卡', value: 2 },
],
allowClear: true,
},
@@ -94,11 +138,9 @@ export function useWithdrawalFormSchema(): VbenFormSchema[] {
component: 'Select',
componentProps: {
options: [
{ label: '待审核', value: 'pending' },
{ label: '已通过', value: 'approved' },
{ label: '已拒绝', value: 'rejected' },
{ label: '已打款', value: 'paid' },
{ label: '打款失败', value: 'failed' },
{ label: '申请中', value: 1 },
{ label: '成功', value: 2 },
{ label: '失败', value: 3 },
],
allowClear: true,
},

View File

@@ -1,11 +1,13 @@
<script lang="ts" setup>
import { computed } from 'vue';
import { computed, ref } from 'vue';
import { Page } from '@vben/common-ui';
import { Page, useVbenDrawer } from '@vben/common-ui';
import { Button, Space } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getAgentWithdrawalList } from '#/api/agent';
import ReviewModal from './modules/review-modal.vue';
import { useWithdrawalColumns, useWithdrawalFormSchema } from './data';
interface Props {
@@ -24,13 +26,35 @@ const queryParams = computed(() => ({
...(props.agentId ? { agent_id: props.agentId } : {}),
}));
const [Grid] = useVbenVxeGrid({
// 审核弹窗
const [ReviewDrawer, reviewDrawerApi] = useVbenDrawer({
connectedComponent: ReviewModal,
destroyOnClose: true,
});
// 操作处理
function onActionClick(e: any) {
const { code, row } = e;
switch (code) {
case 'review':
// 打开审核弹窗
reviewDrawerApi.setData(row).open();
break;
}
}
// 刷新列表
function onRefresh() {
gridApi.query();
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useWithdrawalFormSchema(),
submitOnChange: true,
},
gridOptions: {
columns: useWithdrawalColumns(),
columns: useWithdrawalColumns(onActionClick),
proxyConfig: {
ajax: {
query: async ({
@@ -53,12 +77,32 @@ const [Grid] = useVbenVxeGrid({
total: 'total',
},
},
toolbarConfig: {
custom: true,
export: false,
refresh: { code: 'query' },
search: true,
zoom: true,
},
},
});
</script>
<template>
<Page :auto-content-height="!agentId">
<Grid :table-title="agentId ? '提现记录列表' : '所有提现记录'" />
<ReviewDrawer @success="onRefresh" />
<Grid :table-title="agentId ? '提现记录列表' : '所有提现记录'">
<template #operation="{ row }">
<Space>
<Button
v-if="row.withdraw_type === 2 && row.status === 1"
type="link"
@click="onActionClick({ code: 'review', row })"
>
审核
</Button>
</Space>
</template>
</Grid>
</Page>
</template>

View File

@@ -0,0 +1,182 @@
<script lang="ts" setup>
import { computed, ref } from 'vue';
import { useVbenDrawer } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { reviewBankCardWithdrawal } from '#/api/agent';
const emit = defineEmits<{
(e: 'success'): void;
}>();
const formData = ref<any>(null);
const [Form, formApi] = useVbenForm({
schema: [
{
component: 'Input',
fieldName: 'withdraw_no',
label: '提现单号',
componentProps: {
disabled: true,
},
},
{
component: 'InputNumber',
fieldName: 'amount',
label: '提现金额',
componentProps: {
disabled: true,
min: 0,
precision: 2,
style: { width: '100%' },
addonBefore: '¥',
},
},
{
component: 'InputNumber',
fieldName: 'tax_amount',
label: '扣税金额',
componentProps: {
disabled: true,
min: 0,
precision: 2,
style: { width: '100%' },
addonBefore: '¥',
},
},
{
component: 'InputNumber',
fieldName: 'actual_amount',
label: '实际转账金额',
componentProps: {
disabled: true,
min: 0,
precision: 2,
style: { width: '100%' },
addonBefore: '¥',
},
},
{
component: 'Input',
fieldName: 'bank_card_no',
label: '银行卡号',
componentProps: {
disabled: true,
},
},
{
component: 'Input',
fieldName: 'bank_name',
label: '开户支行',
componentProps: {
disabled: true,
},
},
{
component: 'Input',
fieldName: 'payee_name',
label: '收款人姓名',
componentProps: {
disabled: true,
},
},
{
component: 'RadioGroup',
fieldName: 'action',
label: '审核操作',
defaultValue: 1,
componentProps: {
options: [
{ label: '确认提现', value: 1 },
{ label: '拒绝提现', value: 2 },
],
},
rules: 'required',
},
{
component: 'Textarea',
fieldName: 'remark',
label: '备注',
componentProps: {
rows: 4,
placeholder: '拒绝提现时,请填写拒绝原因',
maxLength: 200,
showCount: true,
style: { width: '100%' },
},
},
],
showDefaultActions: false,
});
const [Drawer, drawerApi] = useVbenDrawer({
async onConfirm() {
const { valid } = await formApi.validate();
if (!valid) return;
if (!formData.value?.id) {
message.error('提现记录ID不存在');
return;
}
const values = await formApi.getValues<{
action: 1 | 2;
remark: string;
}>();
// 验证拒绝时必须填写原因
if (values.action === 2 && !values.remark?.trim()) {
message.error('拒绝提现必须填写拒绝原因');
return;
}
try {
await reviewBankCardWithdrawal({
action: values.action,
remark: values.remark || '',
withdrawal_id: formData.value.id,
});
message.success(values.action === 1 ? '确认提现成功' : '拒绝提现成功');
drawerApi.close();
emit('success');
} catch (error: any) {
message.error(error?.message || '操作失败');
}
},
onOpenChange(isOpen) {
if (isOpen) {
const data = drawerApi.getData<any>();
formApi.resetForm();
if (data) {
formData.value = data;
// 设置表单初始值
formApi.setValues({
withdraw_no: data.withdraw_no || '',
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, // 默认选择确认
remark: '',
});
}
}
},
});
const getDrawerTitle = computed(() => {
return `银行卡提现审核 ${formData.value?.withdraw_no || ''}`;
});
</script>
<template>
<Drawer :title="getDrawerTitle" width="600">
<Form />
</Drawer>
</template>

View File

@@ -10,10 +10,10 @@ export default defineConfig(async () => {
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
// mock代理目标地址
// target: 'http://localhost:8888/api',
target: 'http://localhost:8888/api',
// target: 'https://www.tianyuandb.com/api',
// target: 'https://www.zhinengcha.cn/api',
target: 'https://www.quannengcha./api',
// target: 'https://www.quannengcha./api',
ws: true,
},
},