feat(shop): 新增分销商提现管理功能

- 在 shopDealerWithdraw 模型中增加用户真实姓名、昵称、手机号、头像字段- 增加微信账号和姓名字段用于微信提现- 添加支付凭证图片和备注字段以支持打款记录
- 修改 auditTime 类型为 any以适配多种时间格式
- 新增 shopDealerWithdraw 页面及组件实现提现申请的增删改查
- 实现提现申请的状态管理(待审核、审核通过、驳回、已打款)- 支持根据不同打款方式(微信、支付宝、银行卡)展示相应信息
- 添加提现记录的导入功能组件
- 实现提现申请的批量删除与状态更新操作
- 增加表格列显示用户信息、收款信息、创建时间等关键数据
- 提供编辑弹窗用于查看和修改提现申请详情
- 引入搜索组件优化提现记录筛选体验
This commit is contained in:
2025-10-24 11:34:10 +08:00
parent e1f401808a
commit d822b7b857
33 changed files with 4475 additions and 3579 deletions

View File

@@ -0,0 +1,83 @@
<!-- 经销商订单导入弹窗 -->
<template>
<ele-modal
:width="520"
:footer="null"
title="导入分销订单"
:visible="visible"
@update:visible="updateVisible"
>
<a-spin :spinning="loading">
<a-upload-dragger
accept=".xls,.xlsx"
:show-upload-list="false"
:customRequest="doUpload"
style="padding: 24px 0; margin-bottom: 16px"
>
<p class="ant-upload-drag-icon">
<cloud-upload-outlined/>
</p>
<p class="ant-upload-hint">将文件拖到此处或点击上传</p>
</a-upload-dragger>
<div class="ant-upload-text text-gray-400">
<div>1必须按<a href="https://oss.wsdns.cn/20251018/408b805ec3cd4084a4dc686e130af578.xlsx" target="_blank">导入模版</a>的格式上传</div>
<div>2导入成功确认结算完成佣金的发放</div>
</div>
</a-spin>
</ele-modal>
</template>
<script lang="ts" setup>
import {ref} from 'vue';
import {message} from 'ant-design-vue/es';
import {CloudUploadOutlined} from '@ant-design/icons-vue';
import {importSdyDealerOrder} from "@/api/sdy/sdyDealerOrder";
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
defineProps<{
// 是否打开弹窗
visible: boolean;
}>();
// 导入请求状态
const loading = ref(false);
/* 上传 */
const doUpload = ({file}) => {
if (
![
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
].includes(file.type)
) {
message.error('只能选择 excel 文件');
return false;
}
if (file.size / 1024 / 1024 > 10) {
message.error('大小不能超过 10MB');
return false;
}
loading.value = true;
importSdyDealerOrder(file)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
return false;
};
/* 更新 visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
</script>

View File

@@ -0,0 +1,106 @@
<template>
<div class="flex items-center gap-20">
<!-- 搜索表单 -->
<a-form
:model="where"
layout="inline"
class="search-form"
@finish="handleSearch"
>
<a-form-item>
<a-space>
<a-button
danger
class="ele-btn-icon"
v-if="selection.length > 0"
:disabled="selection?.length === 0"
@click="removeBatch"
>
<template #icon>
<DeleteOutlined/>
</template>
<span>批量删除</span>
</a-button>
</a-space>
</a-form-item>
<a-form-item>
<a-space>
<a-input-search
allow-clear
placeholder="请输入用户ID"
style="width: 240px"
v-model:value="where.keywords"
@search="handleSearch"
/>
<a-button @click="resetSearch">
重置
</a-button>
</a-space>
</a-form-item>
</a-form>
</div>
<!-- 导入弹窗 -->
<Import v-model:visible="showImport" @done="emit('importDone')"/>
</template>
<script lang="ts" setup>
import {ref} from 'vue';
import {
DeleteOutlined
} from '@ant-design/icons-vue';
import Import from './Import.vue';
import useSearch from "@/utils/use-search";
import {ShopDealerWithdrawParam} from "@/api/shop/shopDealerWithdraw/model";
withDefaults(
defineProps<{
// 选中的数据
selection?: any[];
}>(),
{
selection: () => []
}
);
const emit = defineEmits<{
(e: 'search', where?: ShopDealerWithdrawParam): void;
(e: 'batchSettle'): void;
(e: 'export'): void;
(e: 'importDone'): void;
(e: 'remove'): void;
}>();
// 是否显示导入弹窗
const showImport = ref(false);
// 搜索表单
const {where, resetFields} = useSearch<ShopDealerWithdrawParam>({
keywords: '',
});
// 搜索
const handleSearch = () => {
const searchParams = {...where};
// 清除空值
Object.keys(searchParams).forEach(key => {
if (searchParams[key] === '' || searchParams[key] === undefined) {
delete searchParams[key];
}
});
emit('search', searchParams);
};
// 重置搜索
const resetSearch = () => {
resetFields();
emit('search', {});
};
// 批量删除
const removeBatch = () => {
emit('remove');
};
</script>

View File

@@ -0,0 +1,682 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="1000"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑提现申请' : '新增提现申请'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
>
<!-- 基本信息 -->
<a-divider orientation="left">
<span style="color: #1890ff; font-weight: 600;">基本信息</span>
</a-divider>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="备注" name="comments">
<div class="text-red-500">{{ form.comments }}</div>
</a-form-item>
</a-col>
</a-row>
<!-- 收款信息 -->
<a-divider orientation="left">
<span style="color: #1890ff; font-weight: 600;">收款信息</span>
</a-divider>
<!-- 微信收款信息 -->
<div v-if="form.payType === 10" class="payment-info wechat-info">
<a-alert
message="微信收款信息"
description="请确保微信账号信息准确,以免影响到账"
type="success"
show-icon
style="margin-bottom: 16px"
/>
<a-form-item label="微信号" name="wechatAccount">
<a-input
placeholder="请输入微信号"
v-model:value="form.wechatAccount"
/>
</a-form-item>
<a-form-item label="微信昵称" name="wechatName">
<a-input
placeholder="请输入微信昵称"
v-model:value="form.wechatName"
/>
</a-form-item>
</div>
<!-- 支付宝收款信息 -->
<div v-if="form.payType === 20" class="payment-info alipay-info">
<a-alert
message="支付宝收款信息"
description="请确保支付宝账号信息准确,姓名需与实名认证一致"
type="info"
show-icon
style="margin-bottom: 16px"
/>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="支付宝姓名" name="alipayName">
<a-input
placeholder="请输入支付宝实名姓名"
disabled
v-model:value="form.alipayName"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="支付宝账号" name="alipayAccount">
<a-input
placeholder="请输入支付宝账号"
disabled
v-model:value="form.alipayAccount"
/>
</a-form-item>
</a-col>
</a-row>
</div>
<!-- 银行卡收款信息 -->
<div v-if="form.payType === 30" class="payment-info bank-info">
<a-alert
message="银行卡收款信息"
description="请确保银行卡信息准确,开户名需与身份证姓名一致"
type="warning"
show-icon
style="margin-bottom: 16px"
/>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="开户行名称" name="bankName">
{{ form.bankName }}
</a-form-item>
<a-form-item label="银行开户名" name="bankAccount">
{{ form.bankAccount }}
</a-form-item>
<a-form-item label="银行卡号" name="bankCard">
{{ form.bankCard }}
</a-form-item>
</a-col>
</a-row>
</div>
<!-- 审核信息 -->
<a-divider orientation="left">
<span style="color: #1890ff; font-weight: 600;">审核信息</span>
</a-divider>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="申请状态" name="applyStatus">
<a-select v-model:value="form.applyStatus" :disabled="form.applyStatus == 40 || form.applyStatus == 30"
placeholder="请选择申请状态">
<a-select-option :value="10">
<div class="status-option">
<a-tag color="orange">待审核</a-tag>
<span>等待审核</span>
</div>
</a-select-option>
<a-select-option :value="20">
<div class="status-option">
<a-tag color="success">审核通过</a-tag>
<span>审核通过</span>
</div>
</a-select-option>
<a-select-option :value="30">
<div class="status-option">
<a-tag color="error">审核驳回</a-tag>
<span>审核驳回</span>
</div>
</a-select-option>
<a-select-option :value="40">
<div class="status-option">
<a-tag>已打款</a-tag>
<span>已完成打款</span>
</div>
</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="驳回原因" name="rejectReason" v-if="form.applyStatus === 30">
<a-textarea
v-model:value="form.rejectReason"
placeholder="请输入驳回原因"
:rows="3"
:maxlength="200"
show-count
/>
</a-form-item>
<a-form-item label="上传支付凭证" name="image" v-if="form.applyStatus === 40">
<SelectFile
:placeholder="`请选择图片`"
:limit="2"
:data="files"
@done="chooseFile"
@del="onDeleteFile"
/>
</a-form-item>
</a-col>
</a-row>
<!-- 提现预览 -->
<div class="withdraw-preview" v-if="form.money && form.payType">
<a-alert
:type="getPreviewAlertType()"
:message="getPreviewText()"
show-icon
style="margin-top: 16px"
/>
</div>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import {ref, reactive, watch} from 'vue';
import {Form, message} from 'ant-design-vue';
import {assignObject, uuid} from 'ele-admin-pro';
import {addShopDealerWithdraw, updateShopDealerWithdraw} from '@/api/shop/shopDealerWithdraw';
import {ShopDealerWithdraw} from '@/api/shop/shopDealerWithdraw/model';
import {FormInstance} from 'ant-design-vue/es/form';
import {ItemType} from 'ele-admin-pro/es/ele-image-upload/types';
import dayjs from 'dayjs';
import {FileRecord} from "@/api/system/file/model";
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: ShopDealerWithdraw | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref<boolean>(false);
const isSuccess = ref<boolean>(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
const files = ref<ItemType[]>([]);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
// 表单数据
const form = reactive<ShopDealerWithdraw>({
id: undefined,
userId: undefined,
realName: undefined,
nickname: undefined,
phone: undefined,
avatar: undefined,
money: undefined,
payType: undefined,
// 微信相关
wechatAccount: '',
wechatName: '',
// 支付宝相关
alipayName: '',
alipayAccount: '',
// 银行卡相关
bankName: '',
bankAccount: '',
bankCard: '',
// 审核相关
applyStatus: 10,
auditTime: undefined,
rejectReason: '',
platform: '',
comments: '',
tenantId: undefined,
createTime: undefined,
updateTime: undefined
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
userId: [
{
required: true,
message: '请输入分销商用户ID',
trigger: 'blur'
}
],
money: [
{
required: true,
message: '请输入提现金额',
trigger: 'blur'
},
{
validator: (rule: any, value: any) => {
if (value && value <= 0) {
return Promise.reject('提现金额必须大于0');
}
return Promise.resolve();
},
trigger: 'blur'
}
],
payType: [
{
required: true,
message: '请选择打款方式',
trigger: 'change'
}
],
platform: [
{
required: true,
message: '请选择来源平台',
trigger: 'change'
}
],
// 微信验证
wechatAccount: [
{
validator: (rule: any, value: any) => {
if (form.payType === 10 && !value) {
return Promise.reject('请输入微信号');
}
return Promise.resolve();
},
trigger: 'blur'
}
],
wechatName: [
{
validator: (rule: any, value: any) => {
if (form.payType === 10 && !value) {
return Promise.reject('请输入微信昵称');
}
return Promise.resolve();
},
trigger: 'blur'
}
],
// 支付宝验证
alipayName: [
{
validator: (rule: any, value: any) => {
if (form.payType === 20 && !value) {
return Promise.reject('请输入支付宝姓名');
}
return Promise.resolve();
},
trigger: 'blur'
}
],
alipayAccount: [
{
validator: (rule: any, value: any) => {
if (form.payType === 20 && !value) {
return Promise.reject('请输入支付宝账号');
}
return Promise.resolve();
},
trigger: 'blur'
}
],
// 银行卡验证
bankName: [
{
validator: (rule: any, value: any) => {
if (form.payType === 30 && !value) {
return Promise.reject('请输入开户行名称');
}
return Promise.resolve();
},
trigger: 'blur'
}
],
bankAccount: [
{
validator: (rule: any, value: any) => {
if (form.payType === 30 && !value) {
return Promise.reject('请输入银行开户名');
}
return Promise.resolve();
},
trigger: 'blur'
}
],
bankCard: [
{
validator: (rule: any, value: any) => {
if (form.payType === 30 && !value) {
return Promise.reject('请输入银行卡号');
}
if (form.payType === 30 && value && !/^\d{16,19}$/.test(value)) {
return Promise.reject('银行卡号格式不正确');
}
return Promise.resolve();
},
trigger: 'blur'
}
],
applyStatus: [
{
required: true,
message: '请选择申请状态',
trigger: 'change'
}
],
rejectReason: [
{
validator: (rule: any, value: any) => {
if (form.applyStatus === 30 && !value) {
return Promise.reject('驳回时必须填写驳回原因');
}
return Promise.resolve();
},
trigger: 'blur'
}
],
image: [
{
required: true,
message: '请上传打款凭证',
trigger: 'change'
}
]
});
/* 打款方式改变时的处理 */
const onPayTypeChange = (e: any) => {
const payType = e.target.value;
// 清空其他支付方式的信息
if (payType !== 10) {
form.alipayAccount = '';
form.alipayName = '';
}
if (payType !== 20) {
form.alipayName = '';
form.alipayAccount = '';
}
if (payType !== 30) {
form.bankName = '';
form.bankAccount = '';
form.bankCard = '';
}
};
const chooseFile = (data: FileRecord) => {
files.value.push({
uid: data.id,
url: data.url,
status: 'done'
});
form.image = JSON.stringify(files.value.map((d) => d.url));
};
const onDeleteFile = (index: number) => {
files.value.splice(index, 1);
};
/* 获取预览提示类型 */
const getPreviewAlertType = () => {
if (!form.applyStatus) return 'info';
switch (form.applyStatus) {
case 10:
return 'processing';
case 20:
return 'success';
case 30:
return 'error';
case 40:
return 'success';
default:
return 'info';
}
};
/* 获取预览文本 */
const getPreviewText = () => {
if (!form.money || !form.payType) return '';
const amount = parseFloat(form.money.toString()).toFixed(2);
const payTypeMap = {
10: '微信',
20: '支付宝',
30: '银行卡'
};
const statusMap = {
10: '待审核',
20: '审核通过',
30: '审核驳回',
40: '已打款'
};
const payTypeName = payTypeMap[form.payType] || '未知方式';
const statusName = statusMap[form.applyStatus] || '未知状态';
return `提现金额:¥${amount},打款方式:${payTypeName},当前状态:${statusName}`;
};
const {resetFields} = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
if (isSuccess.value) {
console.log('isSuccess')
updateVisible(false);
emit('done');
return;
}
if (form.realName == '' || form.realName == null) {
message.error('该用户未完成实名认证!');
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
// 处理时间字段转换
if (formData.auditTime && dayjs.isDayjs(formData.auditTime)) {
formData.auditTime = formData.auditTime.valueOf();
}
// 根据支付方式清理不相关字段
if (formData.payType !== 10) {
delete formData.wechatAccount;
delete formData.wechatName;
}
if (formData.payType !== 20) {
delete formData.alipayName;
delete formData.alipayAccount;
}
if (formData.payType !== 30) {
delete formData.bankName;
delete formData.bankAccount;
delete formData.bankCard;
}
const saveOrUpdate = isUpdate.value ? updateShopDealerWithdraw : addShopDealerWithdraw;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {
});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
files.value = [];
if (props.data) {
assignObject(form, props.data);
// 处理时间字段
if (props.data.auditTime) {
form.auditTime = dayjs(props.data.auditTime);
}
if (props.data.image) {
const arr = JSON.parse(props.data.image);
arr.map((url: string) => {
files.value.push({
uid: uuid(),
url: url,
status: 'done'
});
});
isSuccess.value = true;
}
isUpdate.value = true;
} else {
// 重置为默认值
Object.assign(form, {
id: undefined,
userId: undefined,
money: undefined,
payType: undefined,
wechatAccount: '',
wechatName: '',
alipayName: '',
alipayAccount: '',
bankName: '',
bankAccount: '',
bankCard: '',
applyStatus: 10,
auditTime: undefined,
rejectReason: '',
platform: '',
image: '',
tenantId: undefined,
createTime: undefined,
updateTime: undefined
});
isUpdate.value = false;
}
} else {
resetFields();
}
},
{immediate: true}
);
</script>
<style lang="less" scoped>
.platform-option,
.status-option {
display: flex;
align-items: center;
.ant-tag {
margin-right: 8px;
}
span {
color: #666;
font-size: 12px;
}
}
.payment-info {
background: #fafafa;
padding: 16px;
border-radius: 6px;
margin-bottom: 16px;
&.wechat-info {
border-left: 3px solid #52c41a;
}
&.alipay-info {
border-left: 3px solid #1890ff;
}
&.bank-info {
border-left: 3px solid #faad14;
}
}
.withdraw-preview {
:deep(.ant-alert) {
.ant-alert-message {
font-weight: 600;
font-size: 14px;
}
}
}
:deep(.ant-divider-horizontal.ant-divider-with-text-left) {
margin: 24px 0 16px 0;
.ant-divider-inner-text {
padding: 0 16px 0 0;
}
}
:deep(.ant-form-item) {
margin-bottom: 16px;
}
:deep(.ant-radio) {
display: flex;
align-items: center;
margin-bottom: 8px;
.ant-radio-inner {
margin-right: 8px;
}
}
:deep(.ant-select-selection-item) {
display: flex;
align-items: center;
}
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-alert) {
.ant-alert-message {
font-weight: 600;
}
}
</style>

View File

@@ -0,0 +1,439 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="shopDealerWithdrawId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'applyStatus'">
<a-tag v-if="record.applyStatus === 10" color="orange">待审核</a-tag>
<a-tag v-if="record.applyStatus === 20" color="success">审核通过</a-tag>
<a-tag v-if="record.applyStatus === 30" color="error">已驳回</a-tag>
<a-tag v-if="record.applyStatus === 40">已打款</a-tag>
</template>
<template v-if="column.key === 'userInfo'">
<a-space>
<a-avatar :src="record.avatar" />
<div class="flex flex-col">
<span>{{ record.realName || '未实名认证' }}</span>
<span class="text-gray-400">{{ record.phone }}</span>
</div>
</a-space>
</template>
<template v-if="column.key === 'paymentInfo'">
<template v-if="record.payType === 10">
<a-space direction="vertical">
<a-tag color="blue">微信</a-tag>
<span>{{ record.wechatName }}</span>
<span>{{ record.wechatName }}</span>
</a-space>
</template>
<template v-if="record.payType === 20">
<a-space direction="vertical">
<a-tag color="blue">支付宝</a-tag>
<span>{{ record.alipayName }}</span>
<span>{{ record.alipayAccount }}</span>
</a-space>
</template>
<template v-if="record.payType === 30">
<a-space direction="vertical">
<a-tag color="blue">银行卡</a-tag>
<span>{{ record.bankName }}</span>
<span>{{ record.bankAccount }}</span>
<span>{{ record.bankCard }}</span>
</a-space>
</template>
</template>
<template v-if="column.key === 'comments'">
<template v-if="record.applyStatus === 30">
<div class="text-red-500">驳回原因{{ record.rejectReason }}</div>
</template>
<template v-if="record.applyStatus === 40 && record.image">
<a-image v-for="(item,index) in JSON.parse(record.image)" :key="index" :src="item" :width="50"
:height="50"/>
</template>
</template>
<template v-if="column.key === 'createTime'">
<a-space direction="vertical">
<a-tooltip title="创建时间">{{ record.createTime }}</a-tooltip>
<a-tooltip title="审核/打款时间" class="text-green-500">{{ record.auditTime }}</a-tooltip>
</a-space>
</template>
<template v-if="column.key === 'action'">
<template v-if="record.applyStatus !== 40">
<a @click="openEdit(record)" class="ele-text-primary">
<EditOutlined/>
编辑
</a>
<a-divider type="vertical"/>
<a-popconfirm
title="确定要删除此提现记录吗?"
@confirm="remove(record)"
placement="topRight"
>
<a class="ele-text-danger">
<DeleteOutlined/>
删除
</a>
</a-popconfirm>
</template>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<ShopDealerWithdrawEdit v-model:visible="showEdit" :data="current" @done="reload"/>
</a-page-header>
</template>
<script lang="ts" setup>
import {createVNode, ref} from 'vue';
import {message, Modal} from 'ant-design-vue';
import {
ExclamationCircleOutlined,
CheckOutlined,
CloseOutlined,
DollarOutlined,
EditOutlined,
DeleteOutlined
} from '@ant-design/icons-vue';
import type {EleProTable} from 'ele-admin-pro';
import {toDateString} from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import {getPageTitle} from '@/utils/common';
import ShopDealerWithdrawEdit from './components/shopDealerWithdrawEdit.vue';
import {
pageShopDealerWithdraw,
removeShopDealerWithdraw,
removeBatchShopDealerWithdraw,
updateShopDealerWithdraw
} from '@/api/shop/shopDealerWithdraw';
import type {ShopDealerWithdraw, ShopDealerWithdrawParam} from '@/api/shop/shopDealerWithdraw/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<ShopDealerWithdraw[]>([]);
// 当前编辑数据
const current = ref<ShopDealerWithdraw | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageShopDealerWithdraw({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: '用户ID',
dataIndex: 'userId',
key: 'userId',
align: 'center',
width: 90,
fixed: 'left'
},
{
title: '提现金额',
dataIndex: 'money',
key: 'money',
align: 'center',
width: 150,
customRender: ({text}) => {
const amount = parseFloat(text || '0').toFixed(2);
return {
type: 'span',
children: `¥${amount}`
};
}
},
{
title: '用户信息',
dataIndex: 'userInfo',
key: 'userInfo'
},
{
title: '收款信息',
dataIndex: 'paymentInfo',
key: 'paymentInfo'
},
// {
// title: '审核时间',
// dataIndex: 'auditTime',
// key: 'auditTime',
// align: 'center',
// width: 120,
// customRender: ({ text }) => text ? toDateString(new Date(text), 'yyyy-MM-dd HH:mm') : '-'
// },
// {
// title: '驳回原因',
// dataIndex: 'rejectReason',
// key: 'rejectReason',
// align: 'left',
// ellipsis: true,
// customRender: ({ text }) => text || '-'
// },
// {
// title: '来源平台',
// dataIndex: 'platform',
// key: 'platform',
// align: 'center',
// width: 100,
// customRender: ({ text }) => text || '-'
// },
{
title: '备注',
dataIndex: 'comments',
key: 'comments',
},
{
title: '申请状态',
dataIndex: 'applyStatus',
key: 'applyStatus',
align: 'center',
width: 150
},
// {
// title: '驳回原因',
// dataIndex: 'rejectReason',
// key: 'rejectReason',
// align: 'center',
// },
// {
// title: '来源客户端',
// dataIndex: 'platform',
// key: 'platform',
// align: 'center',
// },
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
width: 180,
sorter: true,
ellipsis: true,
customRender: ({text}) => toDateString(text, 'yyyy-MM-dd HH:mm:ss')
},
// {
// title: '操作',
// key: 'action',
// width: 180,
// fixed: 'right',
// align: 'center',
// hideInSetting: true
// }
]);
/* 搜索 */
const reload = (where?: ShopDealerWithdrawParam) => {
selection.value = [];
tableRef?.value?.reload({where: where});
};
/* 审核通过 */
const approveWithdraw = (row: ShopDealerWithdraw) => {
Modal.confirm({
title: '审核通过确认',
content: `已核对信息进行核对,正确无误!`,
icon: createVNode(CheckOutlined),
okText: '确认通过',
okType: 'primary',
cancelText: '取消',
onOk: () => {
const hide = message.loading('正在处理审核...', 0);
// 这里需要调用审核通过的API
setTimeout(() => {
hide();
updateShopDealerWithdraw({
id: row.id,
applyStatus: 20
});
message.success('审核通过成功');
reload();
}, 1000);
}
});
};
/* 审核驳回 */
const rejectWithdraw = (row: ShopDealerWithdraw) => {
let rejectReason = '';
Modal.confirm({
title: '审核驳回',
content: createVNode('div', null, [
createVNode('p', null, `用户ID: ${row.userId}`),
createVNode('p', null, `提现金额: ¥${parseFloat(row.money || '0').toFixed(2)}`),
createVNode('p', {style: 'margin-top: 12px;'}, '请输入驳回原因:'),
createVNode('textarea', {
placeholder: '请输入驳回原因...',
style: 'width: 100%; height: 80px; margin-top: 8px; padding: 8px; border: 1px solid #d9d9d9; border-radius: 4px;',
onInput: (e: any) => {
rejectReason = e.target.value;
}
})
]),
icon: createVNode(CloseOutlined),
okText: '确认驳回',
okType: 'danger',
cancelText: '取消',
onOk: () => {
if (!rejectReason.trim()) {
message.error('请输入驳回原因');
return Promise.reject();
}
const hide = message.loading('正在处理审核...', 0);
setTimeout(() => {
hide();
message.success('审核驳回成功');
reload();
}, 1000);
}
});
};
/* 确认打款 */
const confirmPayment = (row: ShopDealerWithdraw) => {
Modal.confirm({
title: '确认打款',
content: `确定已向用户${row.bankAccount}完成打款?此操作不可撤销`,
icon: createVNode(DollarOutlined),
okText: '确认打款',
okType: 'primary',
cancelText: '取消',
onOk: () => {
const hide = message.loading('正在确认打款...', 0);
setTimeout(() => {
updateShopDealerWithdraw({
id: row.id,
applyStatus: 40
})
hide();
message.success('打款确认成功');
reload();
}, 1000);
}
});
};
/* 打开编辑弹窗 */
const openEdit = (row?: ShopDealerWithdraw) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: ShopDealerWithdraw) => {
const hide = message.loading('请求中..', 0);
removeShopDealerWithdraw(row.id)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchShopDealerWithdraw(selection.value.map((d) => d.id))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: ShopDealerWithdraw) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'ShopDealerWithdraw'
};
</script>
<style lang="less" scoped></style>