add recharge check api

This commit is contained in:
mazh 2026-04-25 20:50:37 +08:00
parent 99e1f7e2c3
commit bec8cfb81c
3 changed files with 192 additions and 261 deletions

View File

@ -0,0 +1,6 @@
import { request } from '@/api';
// 获取充值审核列表
export const getRechargeCheckListApi = (params: any) => {
return request.get('/user/rechargeReviewList', { ...params })
}

View File

@ -1,36 +1,33 @@
<template> <template>
<div class="rechange-check"> <div class="rechange-check">
<!-- 搜索区域复用项目内搜索组件 --> <!-- 搜索区域默认把当天 0 点到当前时间写入时间输入框 -->
<RechangeSearch :search-form="searchForm" :search-options="searchOptions" @search="handleSearch" @reset="handleReset"> <RechangeSearch :search-form="searchForm" :search-options="searchOptions" @search="handleSearch" @reset="handleReset">
<template #button> <template #button>
<!-- 当前阶段仅完成页面按钮先保留交互占位 --> <!-- 审核动作先保留按钮交互后续可直接接接口 -->
<el-button type="danger" plain @click="handleReject">驳回</el-button> <el-button type="danger" plain @click="handleReject">驳回</el-button>
<el-button type="success" @click="handleApprove">通过</el-button> <el-button type="success" @click="handleApprove">通过</el-button>
<el-button type="primary" plain @click="handleExport">导出</el-button> <el-button type="primary" plain @click="handleExport">导出</el-button>
</template> </template>
</RechangeSearch> </RechangeSearch>
<!-- 列表区域当前使用页面内表格避免改动公共 Table 组件 --> <!-- 列表区域改为真实接口数据仍保留勾选列供后续审核动作使用 -->
<div class="table-wrapper"> <div class="table-wrapper">
<el-table v-loading="loading" :data="tableData" border height="100%" row-key="id" <el-table v-loading="loading" :data="tableData" border height="100%" row-key="id"
@selection-change="handleSelectionChange"> @selection-change="handleSelectionChange">
<!-- 勾选列用于后续审核通过/驳回 -->
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<!-- 序号列根据当前分页动态计算 -->
<el-table-column label="序号" width="70" align="center"> <el-table-column label="序号" width="70" align="center">
<template #default="scope"> <template #default="scope">
{{ (currentPage - 1) * pageSize + scope.$index + 1 }} {{ (currentPage - 1) * pageSize + scope.$index + 1 }}
</template> </template>
</el-table-column> </el-table-column>
<!-- 业务字段列通过配置项统一渲染 -->
<el-table-column v-for="column in tableColumns" :key="column.prop" :prop="column.prop" :label="column.label" <el-table-column v-for="column in tableColumns" :key="column.prop" :prop="column.prop" :label="column.label"
:width="column.width" :align="column.align || 'center'" show-overflow-tooltip /> :min-width="column.width" :align="column.align || 'center'" show-overflow-tooltip />
</el-table> </el-table>
</div> </div>
<!-- 分页区域复用项目内分页组件 --> <!-- 分页区域使用接口返回的 total / current_page / per_page -->
<RechangePagination v-model="currentPage" v-model:pageSizeValue="pageSize" :total="total" <RechangePagination v-model="currentPage" v-model:pageSizeValue="pageSize" :total="total"
@pagination-change="handlePaginationChange" /> @pagination-change="handlePaginationChange" />
</div> </div>
@ -38,296 +35,208 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { computed, onMounted, ref } from 'vue' import { onMounted, ref } from 'vue'
import RechangeSearch from '@/components/common/Search.vue' import RechangeSearch from '@/components/common/Search.vue'
import RechangePagination from '@/components/common/Pagination.vue' import RechangePagination from '@/components/common/Pagination.vue'
import { exportToExcel } from '@/utils/exportToExcel'
import { getRechargeCheckListApi } from '@/api/modules/system/rechargeCheck'
import { rechangeCheckSearch, rechangeCheckTableColumns } from './options' import { rechangeCheckSearch, rechangeCheckTableColumns } from './options'
// // `rechargeOrder`
interface TableRow { interface TableRow {
id: number id: number
account: string orderNo: string
agentName: string userName: string
serialNo: string currency: string
walletAddress: string amount: string
walletType: string realCurrency: string
holderName: string realAmount: string
bankName: string rate: string
bankCode: string status: string
bankAddress: string statusText: string
depositAmount: string rechangeTime: string
depositCurrency: string
arrivalAmount: string
arrivalCurrency: string
exchangeRate: string
type: string
userType: number
} }
// mock //
const mockTableData: TableRow[] = [ interface SearchFormData {
{ orderNo: string
id: 1, userName: string
account: 'U100001', startTime: string
agentName: '一级代理A', endTime: string
serialNo: 'RC202604250001', }
walletAddress: 'TRX8x2f...7Kp9',
walletType: 'TRC20',
holderName: '张三',
bankName: '中国银行',
bankCode: 'BKCHCNBJ',
bankAddress: '上海市浦东新区世纪大道100号',
depositAmount: '1000.00',
depositCurrency: 'USDT',
arrivalAmount: '1000.00',
arrivalCurrency: 'USDT',
exchangeRate: '1.00',
type: '钱包存款',
userType: 1,
},
{
id: 2,
account: 'U100002',
agentName: '一级代理B',
serialNo: 'RC202604250002',
walletAddress: '0x5fa1...ac20',
walletType: 'ERC20',
holderName: '李四',
bankName: '工商银行',
bankCode: 'ICBKCNBJ',
bankAddress: '北京市朝阳区建国路88号',
depositAmount: '5000.00',
depositCurrency: 'USD',
arrivalAmount: '5000.00',
arrivalCurrency: 'USD',
exchangeRate: '1.00',
type: '银行转账',
userType: 1,
},
{
id: 3,
account: 'A200001',
agentName: '总代运营中心',
serialNo: 'RC202604250003',
walletAddress: 'bc1q7m...91fa',
walletType: 'BTC',
holderName: '王五',
bankName: '建设银行',
bankCode: 'PCBCCNBJ',
bankAddress: '深圳市南山区科技园中一路1号',
depositAmount: '20000.00',
depositCurrency: 'CNY',
arrivalAmount: '2750.00',
arrivalCurrency: 'USD',
exchangeRate: '7.27',
type: '线下汇款',
userType: 2,
},
{
id: 4,
account: 'U100003',
agentName: '一级代理C',
serialNo: 'RC202604250004',
walletAddress: 'TRX9m4k...6Ts1',
walletType: 'TRC20',
holderName: '赵六',
bankName: '农业银行',
bankCode: 'ABOCCNBJ',
bankAddress: '广州市天河区珠江新城华夏路18号',
depositAmount: '3000.00',
depositCurrency: 'USDT',
arrivalAmount: '3000.00',
arrivalCurrency: 'USDT',
exchangeRate: '1.00',
type: '钱包存款',
userType: 1,
},
{
id: 5,
account: 'A200002',
agentName: '华东代理中心',
serialNo: 'RC202604250005',
walletAddress: '0x9bd7...b3f2',
walletType: 'ERC20',
holderName: '孙七',
bankName: '招商银行',
bankCode: 'CMBCCNBS',
bankAddress: '杭州市西湖区文三路90号',
depositAmount: '8000.00',
depositCurrency: 'USD',
arrivalAmount: '8000.00',
arrivalCurrency: 'USD',
exchangeRate: '1.00',
type: '银行转账',
userType: 2,
},
{
id: 6,
account: 'U100004',
agentName: '一级代理D',
serialNo: 'RC202604250006',
walletAddress: 'TRX6a1d...3Lm8',
walletType: 'TRC20',
holderName: '周八',
bankName: '交通银行',
bankCode: 'COMMCNSH',
bankAddress: '成都市高新区天府大道北段966号',
depositAmount: '1200.00',
depositCurrency: 'USDT',
arrivalAmount: '1200.00',
arrivalCurrency: 'USDT',
exchangeRate: '1.00',
type: '钱包存款',
userType: 1,
},
{
id: 7,
account: 'U100005',
agentName: '一级代理E',
serialNo: 'RC202604250007',
walletAddress: 'bc1qx8...0de4',
walletType: 'BTC',
holderName: '吴九',
bankName: '浦发银行',
bankCode: 'SPDBCNBS',
bankAddress: '苏州市工业园区星湖街328号',
depositAmount: '15000.00',
depositCurrency: 'CNY',
arrivalAmount: '2063.27',
arrivalCurrency: 'USD',
exchangeRate: '7.27',
type: '线下汇款',
userType: 1,
},
{
id: 8,
account: 'A200003',
agentName: '华南代理中心',
serialNo: 'RC202604250008',
walletAddress: '0x8e2f...d1a0',
walletType: 'ERC20',
holderName: '郑十',
bankName: '民生银行',
bankCode: 'MSBCCNBJ',
bankAddress: '厦门市思明区湖滨南路258号',
depositAmount: '6000.00',
depositCurrency: 'USD',
arrivalAmount: '6000.00',
arrivalCurrency: 'USD',
exchangeRate: '1.00',
type: '银行转账',
userType: 2,
},
]
// //
const searchOptions = ref(rechangeCheckSearch) const searchOptions = ref(rechangeCheckSearch)
const searchForm = ref({ const tableColumns = ref(rechangeCheckTableColumns)
account: '',
userType: 1,
})
// loading // loading
const tableColumns = computed(() => rechangeCheckTableColumns)
const tableData = ref<TableRow[]>([]) const tableData = ref<TableRow[]>([])
const selectedRows = ref<TableRow[]>([]) const selectedRows = ref<TableRow[]>([])
const loading = ref(false) const loading = ref(false)
// //
const total = ref(0) const total = ref(0)
const currentPage = ref(1) const currentPage = ref(1)
const pageSize = ref(10) const pageSize = ref(10)
// mock // /
const applyFilters = () => { const padZero = (value: number) => String(value).padStart(2, '0')
const keyword = searchForm.value.account.trim().toLowerCase()
const filtered = mockTableData.filter((item) => {
const matchAccount = !keyword || item.account.toLowerCase().includes(keyword)
const matchUserType = !searchForm.value.userType || item.userType === searchForm.value.userType
return matchAccount && matchUserType
})
total.value = filtered.length const formatDateTime = (date: Date) => {
const start = (currentPage.value - 1) * pageSize.value const year = date.getFullYear()
const end = start + pageSize.value const month = padZero(date.getMonth() + 1)
tableData.value = filtered.slice(start, end) const day = padZero(date.getDate())
const hours = padZero(date.getHours())
const minutes = padZero(date.getMinutes())
const seconds = padZero(date.getSeconds())
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
} }
// const getTodayStartTime = () => {
// mock const now = new Date()
const getList = () => { now.setHours(0, 0, 0, 0)
return formatDateTime(now)
}
const getCurrentTime = () => formatDateTime(new Date())
// 0
const createDefaultSearchForm = (): SearchFormData => ({
orderNo: '',
userName: '',
startTime: getTodayStartTime(),
endTime: getCurrentTime(),
})
const searchForm = ref<SearchFormData>(createDefaultSearchForm())
//
const getRechargeStatusText = (status: string | number | null | undefined) => {
const statusMap: Record<string, string> = {
'1': '未审核',
'2': '未付款',
'3': '已付款',
'4': '驳回',
}
return statusMap[String(status ?? '')] || '--'
}
// page / page_size / start_time / end_time null
const getList = async () => {
loading.value = true loading.value = true
selectedRows.value = [] selectedRows.value = []
window.setTimeout(() => {
applyFilters() const params = {
page: currentPage.value,
page_size: pageSize.value,
trade_id: searchForm.value.orderNo || null,
start_time: searchForm.value.startTime || getTodayStartTime(),
end_time: searchForm.value.endTime || getCurrentTime(),
user_name: searchForm.value.userName || null,
}
try {
const data: any = await getRechargeCheckListApi(params)
const list = Array.isArray(data?.data) ? data.data : []
tableData.value = list.map((item: any, index: number) => ({
id: Number(item.id ?? index + 1),
orderNo: String(item.trade_id ?? '--'),
userName: item.user?.username ?? '--',
currency: item.recharge_currency?.symbol ?? '--',
amount: String(item.recharge_amount ?? '--'),
realCurrency: item.real_currency?.symbol ?? '--',
realAmount: String(item.real_amount ?? '--'),
rate: String(item.rate ?? '--'),
status: String(item.status ?? ''),
statusText: getRechargeStatusText(item.status),
rechangeTime: item.created_at ?? '--',
}))
total.value = Number(data?.total ?? list.length)
currentPage.value = Number(data?.current_page ?? currentPage.value)
pageSize.value = Number(data?.per_page ?? pageSize.value)
} catch (error) {
tableData.value = []
total.value = 0
console.error('获取充值审核列表失败', error)
} finally {
loading.value = false loading.value = false
}, 120) }
} }
// //
onMounted(() => { onMounted(() => {
getList() getList()
}) })
// //
const handleSearch = () => { const handleSearch = () => {
currentPage.value = 1 currentPage.value = 1
getList() getList()
} }
// //
const handleReset = () => { const handleReset = () => {
searchForm.value = { searchForm.value = createDefaultSearchForm()
account: '',
userType: 1,
}
currentPage.value = 1 currentPage.value = 1
pageSize.value = 10
getList() getList()
} }
// //
const handleSelectionChange = (rows: TableRow[]) => { const handleSelectionChange = (rows: TableRow[]) => {
selectedRows.value = rows selectedRows.value = rows
} }
// //
const handleApprove = () => { const handleApprove = () => {
if (!selectedRows.value.length) { if (!selectedRows.value.length) {
ElMessage.warning('请先选择要通过的记录') ElMessage.warning('请先选择要通过的记录')
return return
} }
ElMessage.success('页面已完成,接口后续接入') ElMessage.success('审核通过接口待接入')
} }
// //
const handleReject = () => { const handleReject = () => {
if (!selectedRows.value.length) { if (!selectedRows.value.length) {
ElMessage.warning('请先选择要驳回的记录') ElMessage.warning('请先选择要驳回的记录')
return return
} }
ElMessage.success('页面已完成,接口后续接入') ElMessage.success('审核驳回接口待接入')
} }
// //
const handleExport = () => { const handleExport = async () => {
ElMessage.success('页面已完成,接口后续接入') await exportToExcel({
tableData: tableData.value,
columns: tableColumns.value,
title: '导出充值审核',
defaultFileNamePrefix: '充值审核',
})
} }
// //
const handlePaginationChange = (page: { currentPage: number; pageSize: number }) => { const handlePaginationChange = (page: { currentPage: number; pageSize: number }) => {
currentPage.value = page.currentPage currentPage.value = page.currentPage
pageSize.value = page.pageSize pageSize.value = page.pageSize
applyFilters() getList()
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.rechange-check { .rechange-check {
height: 100%; height: 100%;
display: flex;
flex-direction: column;
min-width: 0;
padding-bottom: 70px; padding-bottom: 70px;
box-sizing: border-box; box-sizing: border-box;
// //
:deep(.search-container .el-form) { :deep(.search-container .el-form) {
flex-wrap: wrap; flex-wrap: wrap;
align-items: center; align-items: center;
@ -335,19 +244,29 @@ const handlePaginationChange = (page: { currentPage: number; pageSize: number })
:deep(.search-container .el-form-item) { :deep(.search-container .el-form-item) {
flex: 0 0 auto; flex: 0 0 auto;
margin-bottom: 8px;
} }
:deep(.search-container .el-input), :deep(.search-container .el-input),
:deep(.search-container .el-select) { :deep(.search-container .el-select),
:deep(.search-container .el-date-editor) {
width: 100%; width: 100%;
} }
// //
.table-wrapper { .table-wrapper {
height: calc(100vh - 210px); flex: 1;
min-height: 0;
width: 100%;
background: #fff; background: #fff;
border-radius: 6px; border-radius: 6px;
overflow: hidden; overflow: hidden;
} }
:deep(.el-table),
:deep(.el-table__inner-wrapper),
:deep(.el-scrollbar) {
width: 100%;
}
} }
</style> </style>

View File

@ -1,42 +1,48 @@
// 搜索区域配置 // 搜索区域配置:按充值审核接口的实际查询参数保留订单号、用户名、开始时间、结束时间。
export const rechangeCheckSearch = [ export const rechangeCheckSearch = [
{ {
// 存款账户关键词 prop: 'orderNo',
prop: 'account', label: '订单号',
label: '存款账户',
type: 'input' as const, type: 'input' as const,
placeholder: '请输入存款账户', placeholder: '请输入流水号',
width: '220px' width: '220px',
}, },
{ {
// 客户类型1=客户2=代理 prop: 'userName',
prop: 'userType', label: '用户名称',
label: '客户类型', type: 'input' as const,
type: 'select' as const, placeholder: '请输入用户名',
placeholder: '请选择客户类型', width: '200px',
width: '180px', },
options: [ {
{ label: '客户', value: 1 }, prop: 'startTime',
{ label: '代理', value: 2 } label: '开始时间',
] type: 'date' as const,
} dateType: 'datetime' as const,
valueFormat: 'YYYY-MM-DD HH:mm:ss',
placeholder: '请选择开始时间',
width: '220px',
},
{
prop: 'endTime',
label: '结束时间',
type: 'date' as const,
dateType: 'datetime' as const,
valueFormat: 'YYYY-MM-DD HH:mm:ss',
placeholder: '请选择结束时间',
width: '220px',
},
] ]
// 表格列配置 // 表格列配置:返回数据与 `rechargeOrder` 一致,因此按同一份结构展示。
export const rechangeCheckTableColumns = [ export const rechangeCheckTableColumns = [
{ prop: 'account', label: '账户', align: 'center' as const, width: 140 }, { prop: 'orderNo', label: '订单号', align: 'center' as const, width: 220 },
{ prop: 'agentName', label: '所属代理', align: 'center' as const, width: 140 }, { prop: 'userName', label: '用户名', align: 'center' as const, width: 160 },
{ prop: 'serialNo', label: '流水编号', align: 'center' as const, width: 180 }, { prop: 'currency', label: '充值币种', align: 'center' as const, width: 120 },
{ prop: 'walletAddress', label: '钱包地址', align: 'center' as const, width: 220 }, { prop: 'amount', label: '充值金额', align: 'center' as const, width: 120 },
{ prop: 'walletType', label: '钱包类型', align: 'center' as const, width: 120 }, { prop: 'realCurrency', label: '实际币种', align: 'center' as const, width: 120 },
{ prop: 'holderName', label: '账户持有人姓名', align: 'center' as const, width: 160 }, { prop: 'realAmount', label: '实际金额', align: 'center' as const, width: 120 },
{ prop: 'bankName', label: '银行名称', align: 'center' as const, width: 160 }, { prop: 'rate', label: '汇率', align: 'center' as const, width: 100 },
{ prop: 'bankCode', label: '银行识别代码', align: 'center' as const, width: 160 }, { prop: 'statusText', label: '状态', align: 'center' as const, width: 100 },
{ prop: 'bankAddress', label: '银行地址', align: 'center' as const, width: 200 }, { prop: 'rechangeTime', label: '充值时间', align: 'center' as const, width: 180 },
{ prop: 'depositAmount', label: '存款金额', align: 'center' as const, width: 120 },
{ prop: 'depositCurrency', label: '存款币种', align: 'center' as const, width: 120 },
{ prop: 'arrivalAmount', label: '到账金额', align: 'center' as const, width: 120 },
{ prop: 'arrivalCurrency', label: '到账币种', align: 'center' as const, width: 120 },
{ prop: 'exchangeRate', label: '汇率', align: 'center' as const, width: 100 },
{ prop: 'type', label: '类型', align: 'center' as const, width: 100 }
] ]