Initial commit: One Pipe System

完整的管理系统,包含账户管理、卡片管理、套餐管理、财务管理等功能模块。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
sexygoat
2026-01-22 16:35:33 +08:00
commit 222e5bb11a
495 changed files with 145440 additions and 0 deletions

View File

@@ -0,0 +1,210 @@
<template>
<div class="page-content">
<ElCard shadow="never">
<!-- 搜索栏 -->
<ElForm :inline="true" :model="searchForm" class="search-form">
<ElFormItem label="客户账号">
<ElInput v-model="searchForm.accountNo" placeholder="请输入客户账号" clearable style="width: 200px" />
</ElFormItem>
<ElFormItem label="客户名称">
<ElInput v-model="searchForm.customerName" placeholder="请输入客户名称" clearable style="width: 200px" />
</ElFormItem>
<ElFormItem label="客户类型">
<ElSelect v-model="searchForm.customerType" placeholder="请选择" clearable style="width: 150px">
<ElOption label="代理商" value="agent" />
<ElOption label="企业客户" value="enterprise" />
</ElSelect>
</ElFormItem>
<ElFormItem>
<ElButton type="primary" @click="handleSearch">查询</ElButton>
<ElButton @click="handleReset">重置</ElButton>
</ElFormItem>
</ElForm>
<!-- 数据表格 -->
<ArtTable :data="tableData" index stripe>
<template #default>
<ElTableColumn label="客户账号" prop="accountNo" min-width="150" />
<ElTableColumn label="客户名称" prop="customerName" min-width="150" />
<ElTableColumn label="客户类型" prop="customerType" width="120">
<template #default="scope">
<ElTag :type="scope.row.customerType === 'agent' ? 'success' : 'primary'">
{{ scope.row.customerType === 'agent' ? '代理商' : '企业客户' }}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="佣金总额" prop="totalCommission" width="150">
<template #default="scope"> ¥{{ scope.row.totalCommission.toFixed(2) }} </template>
</ElTableColumn>
<ElTableColumn label="可提现金额" prop="availableAmount" width="150">
<template #default="scope">
<span style="color: var(--el-color-success)"> ¥{{ scope.row.availableAmount.toFixed(2) }} </span>
</template>
</ElTableColumn>
<ElTableColumn label="待入账金额" prop="pendingAmount" width="150">
<template #default="scope">
<span style="color: var(--el-color-warning)"> ¥{{ scope.row.pendingAmount.toFixed(2) }} </span>
</template>
</ElTableColumn>
<ElTableColumn label="已提现金额" prop="withdrawnAmount" width="150">
<template #default="scope"> ¥{{ scope.row.withdrawnAmount.toFixed(2) }} </template>
</ElTableColumn>
<ElTableColumn label="提现次数" prop="withdrawCount" width="100" />
<ElTableColumn label="最后提现时间" prop="lastWithdrawTime" width="180" />
<ElTableColumn label="操作" width="180" fixed="right">
<template #default="scope">
<ElButton link type="primary" @click="handleViewDetail(scope.row)">查看详情</ElButton>
<ElButton link type="primary" @click="handleViewFlow(scope.row)">流水记录</ElButton>
</template>
</ElTableColumn>
</template>
</ArtTable>
<!-- 分页 -->
<ElPagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
style="margin-top: 20px; justify-content: flex-end"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</ElCard>
<!-- 详情对话框 -->
<ElDialog v-model="detailDialogVisible" title="客户账号详情" width="800px" align-center>
<ElDescriptions :column="2" border>
<ElDescriptionsItem label="客户账号">{{ currentRow?.accountNo }}</ElDescriptionsItem>
<ElDescriptionsItem label="客户名称">{{ currentRow?.customerName }}</ElDescriptionsItem>
<ElDescriptionsItem label="客户类型">
<ElTag :type="currentRow?.customerType === 'agent' ? 'success' : 'primary'">
{{ currentRow?.customerType === 'agent' ? '代理商' : '企业客户' }}
</ElTag>
</ElDescriptionsItem>
<ElDescriptionsItem label="联系电话">{{ currentRow?.phone }}</ElDescriptionsItem>
<ElDescriptionsItem label="佣金总额">¥{{ currentRow?.totalCommission.toFixed(2) }}</ElDescriptionsItem>
<ElDescriptionsItem label="可提现金额">
<span style="color: var(--el-color-success)"> ¥{{ currentRow?.availableAmount.toFixed(2) }} </span>
</ElDescriptionsItem>
<ElDescriptionsItem label="待入账金额">
<span style="color: var(--el-color-warning)"> ¥{{ currentRow?.pendingAmount.toFixed(2) }} </span>
</ElDescriptionsItem>
<ElDescriptionsItem label="已提现金额">¥{{ currentRow?.withdrawnAmount.toFixed(2) }}</ElDescriptionsItem>
<ElDescriptionsItem label="提现次数">{{ currentRow?.withdrawCount }}</ElDescriptionsItem>
<ElDescriptionsItem label="最后提现时间">{{ currentRow?.lastWithdrawTime }}</ElDescriptionsItem>
<ElDescriptionsItem label="注册时间">{{ currentRow?.createTime }}</ElDescriptionsItem>
<ElDescriptionsItem label="备注" :span="2">{{ currentRow?.remark || '无' }}</ElDescriptionsItem>
</ElDescriptions>
</ElDialog>
</div>
</template>
<script setup lang="ts">
import { ElMessage } from 'element-plus'
defineOptions({ name: 'CustomerAccount' })
const searchForm = reactive({
accountNo: '',
customerName: '',
customerType: ''
})
const pagination = reactive({
page: 1,
pageSize: 20,
total: 100
})
const detailDialogVisible = ref(false)
const currentRow = ref<any>(null)
// 模拟表格数据
const tableData = ref([
{
id: '1',
accountNo: 'ACC20260001',
customerName: '深圳市科技有限公司',
customerType: 'agent',
phone: '13800138000',
totalCommission: 158900.5,
availableAmount: 58900.5,
pendingAmount: 50000.0,
withdrawnAmount: 50000.0,
withdrawCount: 12,
lastWithdrawTime: '2026-01-08 15:00:00',
createTime: '2025-06-01 10:00:00',
remark: '优质代理商'
},
{
id: '2',
accountNo: 'ACC20260002',
customerName: '广州智能设备公司',
customerType: 'enterprise',
phone: '13900139000',
totalCommission: 89600.0,
availableAmount: 35600.0,
pendingAmount: 24000.0,
withdrawnAmount: 30000.0,
withdrawCount: 8,
lastWithdrawTime: '2026-01-05 10:30:00',
createTime: '2025-07-15 14:20:00',
remark: ''
},
{
id: '3',
accountNo: 'ACC20260003',
customerName: '北京物联网代理',
customerType: 'agent',
phone: '13700137000',
totalCommission: 256700.0,
availableAmount: 106700.0,
pendingAmount: 80000.0,
withdrawnAmount: 70000.0,
withdrawCount: 15,
lastWithdrawTime: '2026-01-09 09:15:00',
createTime: '2025-05-10 09:00:00',
remark: '金牌代理商'
}
])
const handleSearch = () => {
ElMessage.success('查询成功')
}
const handleReset = () => {
searchForm.accountNo = ''
searchForm.customerName = ''
searchForm.customerType = ''
ElMessage.info('已重置')
}
const handleViewDetail = (row: any) => {
currentRow.value = row
detailDialogVisible.value = true
}
const handleViewFlow = (row: any) => {
ElMessage.info(`查看 ${row.customerName} 的流水记录`)
}
const handleSizeChange = (size: number) => {
pagination.pageSize = size
ElMessage.info(`每页显示 ${size}`)
}
const handleCurrentChange = (page: number) => {
pagination.page = page
ElMessage.info(`当前第 ${page}`)
}
</script>
<style lang="scss" scoped>
.page-content {
.search-form {
margin-bottom: 20px;
}
}
</style>

View File

@@ -0,0 +1,305 @@
<template>
<div class="page-content">
<!-- 统计卡片 -->
<ElRow :gutter="20" style="margin-bottom: 20px">
<ElCol :xs="24" :sm="12" :lg="6">
<ElCard shadow="hover">
<div class="stat-card">
<div class="stat-icon" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%)">
<i class="iconfont-sys">&#xe71d;</i>
</div>
<div class="stat-content">
<div class="stat-label">佣金总额</div>
<div class="stat-value">¥{{ accountInfo.totalCommission.toFixed(2) }}</div>
</div>
</div>
</ElCard>
</ElCol>
<ElCol :xs="24" :sm="12" :lg="6">
<ElCard shadow="hover">
<div class="stat-card">
<div class="stat-icon" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%)">
<i class="iconfont-sys">&#xe71e;</i>
</div>
<div class="stat-content">
<div class="stat-label">可提现金额</div>
<div class="stat-value">¥{{ accountInfo.availableAmount.toFixed(2) }}</div>
</div>
</div>
</ElCard>
</ElCol>
<ElCol :xs="24" :sm="12" :lg="6">
<ElCard shadow="hover">
<div class="stat-card">
<div class="stat-icon" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)">
<i class="iconfont-sys">&#xe720;</i>
</div>
<div class="stat-content">
<div class="stat-label">待入账金额</div>
<div class="stat-value">¥{{ accountInfo.pendingAmount.toFixed(2) }}</div>
</div>
</div>
</ElCard>
</ElCol>
<ElCol :xs="24" :sm="12" :lg="6">
<ElCard shadow="hover">
<div class="stat-card">
<div class="stat-icon" style="background: linear-gradient(135deg, #fa709a 0%, #fee140 100%)">
<i class="iconfont-sys">&#xe71f;</i>
</div>
<div class="stat-content">
<div class="stat-label">已提现金额</div>
<div class="stat-value">¥{{ accountInfo.withdrawnAmount.toFixed(2) }}</div>
</div>
</div>
</ElCard>
</ElCol>
</ElRow>
<!-- 操作按钮 -->
<ElRow style="margin-bottom: 20px">
<ElButton type="primary" @click="showWithdrawDialog">申请提现</ElButton>
<ElButton @click="viewWithdrawHistory">提现记录</ElButton>
</ElRow>
<!-- 收支流水 -->
<ElCard shadow="never">
<template #header>
<div style="display: flex; justify-content: space-between; align-items: center">
<span style="font-weight: 500">收支流水</span>
<ElRadioGroup v-model="flowType" size="small">
<ElRadioButton value="all">全部</ElRadioButton>
<ElRadioButton value="income">收入</ElRadioButton>
<ElRadioButton value="withdraw">提现</ElRadioButton>
</ElRadioGroup>
</div>
</template>
<ArtTable :data="filteredFlowData" index max-height="500">
<template #default>
<ElTableColumn label="流水号" prop="flowNo" min-width="180" />
<ElTableColumn label="类型" prop="type">
<template #default="scope">
<ElTag :type="scope.row.type === 'income' ? 'success' : 'warning'">
{{ scope.row.type === 'income' ? '收入' : '提现' }}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="金额" prop="amount">
<template #default="scope">
<span :style="{ color: scope.row.type === 'income' ? 'var(--el-color-success)' : 'var(--el-color-danger)' }">
{{ scope.row.type === 'income' ? '+' : '-' }}¥{{ scope.row.amount.toFixed(2) }}
</span>
</template>
</ElTableColumn>
<ElTableColumn label="余额" prop="balance">
<template #default="scope"> ¥{{ scope.row.balance.toFixed(2) }} </template>
</ElTableColumn>
<ElTableColumn label="说明" prop="description" show-overflow-tooltip />
<ElTableColumn label="时间" prop="createTime" width="180" />
</template>
</ArtTable>
</ElCard>
<!-- 提现申请对话框 -->
<ElDialog v-model="withdrawDialogVisible" title="申请提现" width="600px" align-center>
<ElForm ref="withdrawFormRef" :model="withdrawForm" :rules="withdrawRules" label-width="120px">
<ElFormItem label="可提现金额">
<span style="color: var(--el-color-success); font-size: 20px; font-weight: 500">
¥{{ accountInfo.availableAmount.toFixed(2) }}
</span>
</ElFormItem>
<ElFormItem label="提现金额" prop="amount">
<ElInputNumber
v-model="withdrawForm.amount"
:min="1"
:max="accountInfo.availableAmount"
:precision="2"
style="width: 100%"
/>
</ElFormItem>
<ElFormItem label="手续费">
<span>¥{{ calculatedFee.toFixed(2) }}</span>
<span style="margin-left: 8px; color: var(--el-text-color-secondary)">
(费率: {{ feeRate * 100 }}%)
</span>
</ElFormItem>
<ElFormItem label="实际到账">
<span style="color: var(--el-color-success); font-size: 18px; font-weight: 500">
¥{{ actualAmount.toFixed(2) }}
</span>
</ElFormItem>
<ElFormItem label="收款银行" prop="bankName">
<ElSelect v-model="withdrawForm.bankName" placeholder="请选择收款银行" style="width: 100%">
<ElOption label="中国工商银行" value="工商银行" />
<ElOption label="中国建设银行" value="建设银行" />
<ElOption label="中国农业银行" value="农业银行" />
<ElOption label="中国银行" value="中国银行" />
<ElOption label="招商银行" value="招商银行" />
</ElSelect>
</ElFormItem>
<ElFormItem label="银行账户" prop="bankAccount">
<ElInput v-model="withdrawForm.bankAccount" placeholder="请输入银行账户" />
</ElFormItem>
<ElFormItem label="开户姓名" prop="accountName">
<ElInput v-model="withdrawForm.accountName" placeholder="请输入开户姓名" />
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="withdrawDialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="submitWithdraw">提交申请</ElButton>
</div>
</template>
</ElDialog>
</div>
</template>
<script setup lang="ts">
import { ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
defineOptions({ name: 'MyAccount' })
const accountInfo = reactive({
totalCommission: 158900.5,
availableAmount: 58900.5,
pendingAmount: 50000.0,
withdrawnAmount: 50000.0
})
const flowType = ref('all')
const withdrawDialogVisible = ref(false)
const withdrawFormRef = ref<FormInstance>()
const feeRate = 0.002 // 手续费率 0.2%
const withdrawForm = reactive({
amount: 0,
bankName: '',
bankAccount: '',
accountName: ''
})
const withdrawRules = reactive<FormRules>({
amount: [
{ required: true, message: '请输入提现金额', trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (value > accountInfo.availableAmount) {
callback(new Error('提现金额不能大于可提现金额'))
} else if (value < 1) {
callback(new Error('提现金额不能小于1元'))
} else {
callback()
}
},
trigger: 'blur'
}
],
bankName: [{ required: true, message: '请选择收款银行', trigger: 'change' }],
bankAccount: [{ required: true, message: '请输入银行账户', trigger: 'blur' }],
accountName: [{ required: true, message: '请输入开户姓名', trigger: 'blur' }]
})
const flowData = ref([
{
id: '1',
flowNo: 'FL202601090001',
type: 'income',
amount: 1580.0,
balance: 58900.5,
description: '套餐销售佣金',
createTime: '2026-01-09 10:30:00'
},
{
id: '2',
flowNo: 'FL202601080001',
type: 'withdraw',
amount: 5000.0,
balance: 57320.5,
description: '提现到账',
createTime: '2026-01-08 15:00:00'
},
{
id: '3',
flowNo: 'FL202601070001',
type: 'income',
amount: 2360.0,
balance: 62320.5,
description: '号卡分配佣金',
createTime: '2026-01-07 14:20:00'
}
])
const calculatedFee = computed(() => {
return withdrawForm.amount * feeRate
})
const actualAmount = computed(() => {
return withdrawForm.amount - calculatedFee.value
})
const filteredFlowData = computed(() => {
if (flowType.value === 'all') return flowData.value
return flowData.value.filter((item) => item.type === flowType.value)
})
const showWithdrawDialog = () => {
withdrawForm.amount = 0
withdrawForm.bankName = ''
withdrawForm.bankAccount = ''
withdrawForm.accountName = ''
withdrawDialogVisible.value = true
}
const submitWithdraw = async () => {
if (!withdrawFormRef.value) return
await withdrawFormRef.value.validate((valid) => {
if (valid) {
ElMessage.success('提现申请提交成功,请等待审核')
withdrawDialogVisible.value = false
}
})
}
const viewWithdrawHistory = () => {
ElMessage.info('查看提现记录')
}
</script>
<style lang="scss" scoped>
.page-content {
.stat-card {
display: flex;
align-items: center;
gap: 16px;
.stat-icon {
width: 60px;
height: 60px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 28px;
}
.stat-content {
flex: 1;
.stat-label {
font-size: 14px;
color: var(--el-text-color-secondary);
margin-bottom: 8px;
}
.stat-value {
font-size: 24px;
font-weight: 600;
color: var(--el-text-color-primary);
}
}
}
}
</style>

View File

@@ -0,0 +1,220 @@
<template>
<div class="page-content">
<ElCard shadow="never">
<template #header>
<span style="font-weight: 500">提现参数配置</span>
</template>
<ElForm ref="formRef" :model="form" :rules="rules" label-width="150px" style="max-width: 800px">
<ElFormItem label="最低提现金额" prop="minAmount">
<ElInputNumber v-model="form.minAmount" :min="1" :precision="2" />
<span style="margin-left: 8px"></span>
</ElFormItem>
<ElFormItem label="手续费模式" prop="feeMode">
<ElRadioGroup v-model="form.feeMode">
<ElRadio value="fixed">固定手续费</ElRadio>
<ElRadio value="percent">比例手续费</ElRadio>
</ElRadioGroup>
</ElFormItem>
<ElFormItem v-if="form.feeMode === 'fixed'" label="固定手续费" prop="fixedFee">
<ElInputNumber v-model="form.fixedFee" :min="0" :precision="2" />
<span style="margin-left: 8px">/</span>
</ElFormItem>
<ElFormItem v-if="form.feeMode === 'percent'" label="手续费比例" prop="feePercent">
<ElInputNumber v-model="form.feePercent" :min="0" :max="100" :precision="2" />
<span style="margin-left: 8px">%</span>
</ElFormItem>
<ElFormItem label="单日提现次数" prop="dailyLimit">
<ElInputNumber v-model="form.dailyLimit" :min="1" :max="10" />
<span style="margin-left: 8px"></span>
</ElFormItem>
<ElFormItem label="提现到账时间" prop="arrivalTime">
<ElSelect v-model="form.arrivalTime" style="width: 200px">
<ElOption label="实时到账" value="realtime" />
<ElOption label="2小时内到账" value="2hours" />
<ElOption label="24小时内到账" value="24hours" />
<ElOption label="T+1到账" value="t1" />
<ElOption label="T+3到账" value="t3" />
</ElSelect>
</ElFormItem>
<ElFormItem label="工作日提现" prop="workdayOnly">
<ElSwitch v-model="form.workdayOnly" />
<span style="margin-left: 8px; color: var(--el-text-color-secondary)">
{{ form.workdayOnly ? '仅工作日可提现' : '每天都可提现' }}
</span>
</ElFormItem>
<ElFormItem label="提现时间段" prop="timeRange">
<ElTimePicker
v-model="form.timeRange"
is-range
range-separator=""
start-placeholder="开始时间"
end-placeholder="结束时间"
format="HH:mm"
/>
</ElFormItem>
<ElFormItem label="配置说明" prop="description">
<ElInput
v-model="form.description"
type="textarea"
:rows="3"
placeholder="请输入配置说明,如配置生效时间等"
/>
</ElFormItem>
<ElFormItem>
<ElButton type="primary" @click="handleSave">保存配置</ElButton>
<ElButton @click="resetForm">重置</ElButton>
</ElFormItem>
</ElForm>
</ElCard>
<ElCard shadow="never" style="margin-top: 20px">
<template #header>
<span style="font-weight: 500">配置历史记录</span>
</template>
<ArtTable :data="historyData" index>
<template #default>
<ElTableColumn label="配置时间" prop="configTime" width="180" />
<ElTableColumn label="最低金额" prop="minAmount">
<template #default="scope"> ¥{{ scope.row.minAmount.toFixed(2) }} </template>
</ElTableColumn>
<ElTableColumn label="手续费" prop="fee">
<template #default="scope">
{{
scope.row.feeMode === 'fixed'
? `¥${scope.row.fixedFee.toFixed(2)}/笔`
: `${scope.row.feePercent}%`
}}
</template>
</ElTableColumn>
<ElTableColumn label="单日限制" prop="dailyLimit">
<template #default="scope"> {{ scope.row.dailyLimit }}/ </template>
</ElTableColumn>
<ElTableColumn label="到账时间" prop="arrivalTime">
<template #default="scope">
{{ getArrivalTimeText(scope.row.arrivalTime) }}
</template>
</ElTableColumn>
<ElTableColumn label="配置人" prop="operator" />
<ElTableColumn label="状态" prop="status">
<template #default="scope">
<ElTag :type="scope.row.status === 'active' ? 'success' : 'info'">
{{ scope.row.status === 'active' ? '当前生效' : '已过期' }}
</ElTag>
</template>
</ElTableColumn>
</template>
</ArtTable>
</ElCard>
</div>
</template>
<script setup lang="ts">
import { ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
defineOptions({ name: 'WithdrawalSettings' })
const formRef = ref<FormInstance>()
const form = reactive({
minAmount: 100,
feeMode: 'percent',
fixedFee: 2,
feePercent: 0.2,
dailyLimit: 3,
arrivalTime: '24hours',
workdayOnly: false,
timeRange: [new Date(2024, 0, 1, 0, 0), new Date(2024, 0, 1, 23, 59)],
description: ''
})
const rules = reactive<FormRules>({
minAmount: [{ required: true, message: '请输入最低提现金额', trigger: 'blur' }],
feeMode: [{ required: true, message: '请选择手续费模式', trigger: 'change' }],
dailyLimit: [{ required: true, message: '请输入单日提现次数', trigger: 'blur' }],
arrivalTime: [{ required: true, message: '请选择到账时间', trigger: 'change' }]
})
const historyData = ref([
{
id: '1',
configTime: '2026-01-09 10:00:00',
minAmount: 100,
feeMode: 'percent',
fixedFee: 0,
feePercent: 0.2,
dailyLimit: 3,
arrivalTime: '24hours',
operator: 'admin',
status: 'active'
},
{
id: '2',
configTime: '2026-01-01 10:00:00',
minAmount: 50,
feeMode: 'fixed',
fixedFee: 2.0,
feePercent: 0,
dailyLimit: 5,
arrivalTime: 't1',
operator: 'admin',
status: 'expired'
}
])
const getArrivalTimeText = (value: string) => {
const map: Record<string, string> = {
realtime: '实时到账',
'2hours': '2小时内',
'24hours': '24小时内',
t1: 'T+1',
t3: 'T+3'
}
return map[value] || '未知'
}
const handleSave = async () => {
if (!formRef.value) return
await formRef.value.validate((valid) => {
if (valid) {
// 添加到历史记录
historyData.value.unshift({
id: Date.now().toString(),
configTime: new Date().toLocaleString('zh-CN'),
...form,
operator: 'admin',
status: 'active'
})
// 将之前的配置标记为过期
historyData.value.slice(1).forEach((item) => {
item.status = 'expired'
})
ElMessage.success('配置保存成功')
}
})
}
const resetForm = () => {
formRef.value?.resetFields()
}
</script>
<style lang="scss" scoped>
.page-content {
:deep(.el-card__header) {
padding: 16px 20px;
border-bottom: 1px solid var(--el-border-color-light);
}
}
</style>

View File

@@ -0,0 +1,351 @@
<template>
<div class="page-content">
<ElRow>
<ElCol :xs="24" :sm="12" :lg="6">
<ElInput v-model="searchQuery" placeholder="申请人/手机号" clearable></ElInput>
</ElCol>
<div style="width: 12px"></div>
<ElCol :xs="24" :sm="12" :lg="6">
<ElSelect v-model="statusFilter" placeholder="审核状态" clearable style="width: 100%">
<ElOption label="待审核" value="pending" />
<ElOption label="已通过" value="approved" />
<ElOption label="已拒绝" value="rejected" />
</ElSelect>
</ElCol>
<div style="width: 12px"></div>
<ElCol :xs="24" :sm="12" :lg="6">
<ElDatePicker
v-model="dateRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
style="width: 100%"
/>
</ElCol>
<div style="width: 12px"></div>
<ElCol :xs="24" :sm="12" :lg="6" class="el-col2">
<ElButton v-ripple @click="handleSearch">搜索</ElButton>
<ElButton v-ripple @click="handleBatchApprove" :disabled="selectedIds.length === 0"
>批量审核</ElButton
>
</ElCol>
</ElRow>
<ArtTable :data="filteredData" index @selection-change="handleSelectionChange">
<template #default>
<ElTableColumn type="selection" width="55" />
<ElTableColumn label="申请单号" prop="orderNo" min-width="180" />
<ElTableColumn label="申请人" prop="applicantName" />
<ElTableColumn label="客户类型" prop="customerType">
<template #default="scope">
<ElTag :type="scope.row.customerType === 'agent' ? '' : 'success'">
{{ scope.row.customerType === 'agent' ? '代理商' : '企业客户' }}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="提现金额" prop="amount">
<template #default="scope">
<span style="color: var(--el-color-danger); font-weight: 500">
¥{{ scope.row.amount.toFixed(2) }}
</span>
</template>
</ElTableColumn>
<ElTableColumn label="手续费" prop="fee">
<template #default="scope"> ¥{{ scope.row.fee.toFixed(2) }} </template>
</ElTableColumn>
<ElTableColumn label="实际到账" prop="actualAmount">
<template #default="scope">
<span style="color: var(--el-color-success); font-weight: 500">
¥{{ scope.row.actualAmount.toFixed(2) }}
</span>
</template>
</ElTableColumn>
<ElTableColumn label="收款账户" prop="bankAccount" show-overflow-tooltip />
<ElTableColumn label="状态" prop="status">
<template #default="scope">
<ElTag :type="getStatusTagType(scope.row.status)">
{{ getStatusText(scope.row.status) }}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="申请时间" prop="createTime" width="180" />
<ElTableColumn fixed="right" label="操作" width="180">
<template #default="scope">
<el-button link @click="viewDetail(scope.row)">详情</el-button>
<el-button
v-if="scope.row.status === 'pending'"
link
type="success"
@click="handleApprove(scope.row)"
>通过</el-button
>
<el-button
v-if="scope.row.status === 'pending'"
link
type="danger"
@click="handleReject(scope.row)"
>拒绝</el-button
>
</template>
</ElTableColumn>
</template>
</ArtTable>
<!-- 详情对话框 -->
<ElDialog v-model="detailDialogVisible" title="提现申请详情" width="700px" align-center>
<ElDescriptions :column="2" border>
<ElDescriptionsItem label="申请单号">{{ currentItem?.orderNo }}</ElDescriptionsItem>
<ElDescriptionsItem label="申请人">{{ currentItem?.applicantName }}</ElDescriptionsItem>
<ElDescriptionsItem label="手机号">{{ currentItem?.phone }}</ElDescriptionsItem>
<ElDescriptionsItem label="客户类型">
{{ currentItem?.customerType === 'agent' ? '代理商' : '企业客户' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="提现金额">
<span style="color: var(--el-color-danger); font-weight: 500">
¥{{ currentItem?.amount.toFixed(2) }}
</span>
</ElDescriptionsItem>
<ElDescriptionsItem label="手续费">¥{{ currentItem?.fee.toFixed(2) }}</ElDescriptionsItem>
<ElDescriptionsItem label="实际到账">
<span style="color: var(--el-color-success); font-weight: 500">
¥{{ currentItem?.actualAmount.toFixed(2) }}
</span>
</ElDescriptionsItem>
<ElDescriptionsItem label="收款银行">{{ currentItem?.bankName }}</ElDescriptionsItem>
<ElDescriptionsItem label="收款账户" :span="2">{{
currentItem?.bankAccount
}}</ElDescriptionsItem>
<ElDescriptionsItem label="开户姓名">{{ currentItem?.accountName }}</ElDescriptionsItem>
<ElDescriptionsItem label="申请时间">{{ currentItem?.createTime }}</ElDescriptionsItem>
<ElDescriptionsItem label="审核状态">
<ElTag :type="getStatusTagType(currentItem?.status || 'pending')">
{{ getStatusText(currentItem?.status || 'pending') }}
</ElTag>
</ElDescriptionsItem>
<ElDescriptionsItem v-if="currentItem?.status !== 'pending'" label="审核时间">{{
currentItem?.auditTime
}}</ElDescriptionsItem>
<ElDescriptionsItem v-if="currentItem?.rejectReason" label="拒绝原因" :span="2">{{
currentItem?.rejectReason
}}</ElDescriptionsItem>
</ElDescriptions>
</ElDialog>
<!-- 拒绝对话框 -->
<ElDialog v-model="rejectDialogVisible" title="拒绝提现" width="500px" align-center>
<ElForm ref="rejectFormRef" :model="rejectForm" :rules="rejectRules" label-width="100px">
<ElFormItem label="拒绝原因" prop="reason">
<ElInput
v-model="rejectForm.reason"
type="textarea"
:rows="4"
placeholder="请输入拒绝原因"
/>
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="rejectDialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="confirmReject">确认拒绝</ElButton>
</div>
</template>
</ElDialog>
</div>
</template>
<script setup lang="ts">
import { ElMessage, ElMessageBox } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
defineOptions({ name: 'WithdrawalManagement' })
interface WithdrawalItem {
id: string
orderNo: string
applicantName: string
phone: string
customerType: 'agent' | 'enterprise'
amount: number
fee: number
actualAmount: number
bankName: string
bankAccount: string
accountName: string
status: 'pending' | 'approved' | 'rejected'
createTime: string
auditTime?: string
rejectReason?: string
}
const mockData = ref<WithdrawalItem[]>([
{
id: '1',
orderNo: 'WD202601090001',
applicantName: '张三',
phone: '13800138001',
customerType: 'agent',
amount: 5000.0,
fee: 10.0,
actualAmount: 4990.0,
bankName: '中国工商银行',
bankAccount: '6222 **** **** 1234',
accountName: '张三',
status: 'pending',
createTime: '2026-01-09 10:00:00'
},
{
id: '2',
orderNo: 'WD202601090002',
applicantName: '李四',
phone: '13800138002',
customerType: 'enterprise',
amount: 3000.0,
fee: 6.0,
actualAmount: 2994.0,
bankName: '中国建设银行',
bankAccount: '6227 **** **** 5678',
accountName: '李四',
status: 'approved',
createTime: '2026-01-08 14:30:00',
auditTime: '2026-01-08 15:00:00'
},
{
id: '3',
orderNo: 'WD202601090003',
applicantName: '王五',
phone: '13800138003',
customerType: 'agent',
amount: 2000.0,
fee: 4.0,
actualAmount: 1996.0,
bankName: '中国农业银行',
bankAccount: '6228 **** **** 9012',
accountName: '王五',
status: 'rejected',
createTime: '2026-01-07 16:20:00',
auditTime: '2026-01-07 17:00:00',
rejectReason: '账户信息与实名不符'
}
])
const searchQuery = ref('')
const statusFilter = ref('')
const dateRange = ref<[Date, Date] | null>(null)
const selectedIds = ref<string[]>([])
const detailDialogVisible = ref(false)
const rejectDialogVisible = ref(false)
const currentItem = ref<WithdrawalItem | null>(null)
const rejectFormRef = ref<FormInstance>()
const rejectForm = reactive({
reason: ''
})
const rejectRules = reactive<FormRules>({
reason: [{ required: true, message: '请输入拒绝原因', trigger: 'blur' }]
})
const filteredData = computed(() => {
let data = mockData.value
if (searchQuery.value) {
data = data.filter(
(item) => item.applicantName.includes(searchQuery.value) || item.phone.includes(searchQuery.value)
)
}
if (statusFilter.value) {
data = data.filter((item) => item.status === statusFilter.value)
}
return data
})
const getStatusText = (status: string) => {
const map: Record<string, string> = {
pending: '待审核',
approved: '已通过',
rejected: '已拒绝'
}
return map[status] || '未知'
}
const getStatusTagType = (status: string) => {
const map: Record<string, string> = {
pending: 'warning',
approved: 'success',
rejected: 'danger'
}
return map[status] || 'info'
}
const handleSearch = () => {}
const handleSelectionChange = (selection: WithdrawalItem[]) => {
selectedIds.value = selection.map((item) => item.id)
}
const viewDetail = (row: WithdrawalItem) => {
currentItem.value = row
detailDialogVisible.value = true
}
const handleApprove = (row: WithdrawalItem) => {
ElMessageBox.confirm(
`确认通过提现申请?金额:¥${row.amount.toFixed(2)},实际到账:¥${row.actualAmount.toFixed(2)}`,
'审核确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'success'
}
).then(() => {
row.status = 'approved'
row.auditTime = new Date().toLocaleString('zh-CN')
ElMessage.success('审核通过')
})
}
const handleReject = (row: WithdrawalItem) => {
currentItem.value = row
rejectForm.reason = ''
rejectDialogVisible.value = true
}
const confirmReject = async () => {
if (!rejectFormRef.value) return
await rejectFormRef.value.validate((valid) => {
if (valid && currentItem.value) {
currentItem.value.status = 'rejected'
currentItem.value.auditTime = new Date().toLocaleString('zh-CN')
currentItem.value.rejectReason = rejectForm.reason
rejectDialogVisible.value = false
ElMessage.success('已拒绝提现申请')
}
})
}
const handleBatchApprove = () => {
ElMessageBox.confirm(`确认批量审核 ${selectedIds.value.length} 条提现申请吗?`, '批量审核', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
selectedIds.value.forEach((id) => {
const item = mockData.value.find((i) => i.id === id)
if (item && item.status === 'pending') {
item.status = 'approved'
item.auditTime = new Date().toLocaleString('zh-CN')
}
})
ElMessage.success('批量审核成功')
selectedIds.value = []
})
}
</script>
<style lang="scss" scoped>
.page-content {
:deep(.el-descriptions__label) {
font-weight: 500;
}
}
</style>