This commit is contained in:
parent
7b3f40069f
commit
5e4329dc5b
|
|
@ -63,8 +63,13 @@
|
||||||
@selection-change="handleSelectionChange"
|
@selection-change="handleSelectionChange"
|
||||||
@current-change="handleCurrentChange"
|
@current-change="handleCurrentChange"
|
||||||
>
|
>
|
||||||
<!-- 勾选列 -->
|
<!-- 勾选列/单选列 -->
|
||||||
<el-table-column v-if="showSelection" type="selection" width="55" align="center" />
|
<el-table-column v-if="showSelection && !singleSelect" type="selection" width="55" align="center" />
|
||||||
|
<el-table-column v-if="showSelection && singleSelect" width="55" align="center" label="">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-radio v-model="selectedRadioRow" :label="getRowKeyValue(row)" @change="handleRadioChange(row)"> </el-radio>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
<!-- 动态表头列 -->
|
<!-- 动态表头列 -->
|
||||||
<template v-for="(column, index) in finalColumns" :key="index">
|
<template v-for="(column, index) in finalColumns" :key="index">
|
||||||
|
|
@ -139,6 +144,7 @@
|
||||||
:label="column.label"
|
:label="column.label"
|
||||||
:width="column.width"
|
:width="column.width"
|
||||||
:align="column.align || 'left'"
|
:align="column.align || 'left'"
|
||||||
|
:fixed="column.fixed"
|
||||||
>
|
>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<slot :name="column.slotName" :row="row" />
|
<slot :name="column.slotName" :row="row" />
|
||||||
|
|
@ -180,7 +186,7 @@ interface TableColumn {
|
||||||
isNumber?: boolean
|
isNumber?: boolean
|
||||||
options?: { label: string; value: string | number }[]
|
options?: { label: string; value: string | number }[]
|
||||||
visible?: boolean
|
visible?: boolean
|
||||||
fixed?: boolean
|
fixed?: boolean | 'left' | 'right'
|
||||||
sortOrder?: number
|
sortOrder?: number
|
||||||
// 表头筛选器相关属性
|
// 表头筛选器相关属性
|
||||||
filters?: { text: string; value: any }[]
|
filters?: { text: string; value: any }[]
|
||||||
|
|
@ -201,6 +207,7 @@ interface Props {
|
||||||
highlightCurrentRow?: boolean
|
highlightCurrentRow?: boolean
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
showSelection?: boolean
|
showSelection?: boolean
|
||||||
|
singleSelect?: boolean // 单选模式
|
||||||
showColumnSetting?: boolean
|
showColumnSetting?: boolean
|
||||||
columnSettingStorageKey?: string
|
columnSettingStorageKey?: string
|
||||||
enableClientFilter?: boolean // 是否启用客户端筛选
|
enableClientFilter?: boolean // 是否启用客户端筛选
|
||||||
|
|
@ -211,11 +218,25 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
highlightCurrentRow: true,
|
highlightCurrentRow: true,
|
||||||
loading: false,
|
loading: false,
|
||||||
showSelection: false,
|
showSelection: false,
|
||||||
|
singleSelect: false,
|
||||||
showColumnSetting: true, // 默认开启列设置
|
showColumnSetting: true, // 默认开启列设置
|
||||||
columnSettingStorageKey: '',
|
columnSettingStorageKey: '',
|
||||||
enableClientFilter: true, // 默认开启客户端筛选
|
enableClientFilter: true, // 默认开启客户端筛选
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 单选模式相关
|
||||||
|
const selectedRadioRow = ref<any>(null)
|
||||||
|
|
||||||
|
// 获取行标识的safe版本
|
||||||
|
const getRowKeyValue = (row: any) => {
|
||||||
|
return props.rowKey ? row[props.rowKey] : row.id
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRadioChange = (row: any) => {
|
||||||
|
selectedRadioRow.value = getRowKeyValue(row)
|
||||||
|
emit('selection-change', [row])
|
||||||
|
}
|
||||||
|
|
||||||
// 获取过滤后的数据
|
// 获取过滤后的数据
|
||||||
const filteredTableData = computed(() => {
|
const filteredTableData = computed(() => {
|
||||||
if (!props.enableClientFilter || Object.keys(filterStates).length === 0) {
|
if (!props.enableClientFilter || Object.keys(filterStates).length === 0) {
|
||||||
|
|
@ -293,10 +314,18 @@ const initColumns = () => {
|
||||||
|
|
||||||
// 合并缓存和原始配置,保持缓存的顺序
|
// 合并缓存和原始配置,保持缓存的顺序
|
||||||
localColumns.value = cachedConfig.map((cached: any) => {
|
localColumns.value = cachedConfig.map((cached: any) => {
|
||||||
|
// 优先按 prop 匹配
|
||||||
const originalCol = props.columns?.find((c) => c.prop === cached.prop)
|
const originalCol = props.columns?.find((c) => c.prop === cached.prop)
|
||||||
if (originalCol) {
|
if (originalCol) {
|
||||||
return { ...originalCol, ...cached }
|
return { ...originalCol, ...cached }
|
||||||
}
|
}
|
||||||
|
// 对于没有 prop 的列(如操作列),按 slotName 匹配
|
||||||
|
if (cached.slotName) {
|
||||||
|
const slotCol = props.columns?.find((c) => c.slotName === cached.slotName)
|
||||||
|
if (slotCol) {
|
||||||
|
return { ...slotCol, ...cached }
|
||||||
|
}
|
||||||
|
}
|
||||||
return null
|
return null
|
||||||
}).filter(Boolean)
|
}).filter(Boolean)
|
||||||
} catch {
|
} catch {
|
||||||
|
|
@ -375,6 +404,26 @@ onMounted(() => {
|
||||||
}, 100)
|
}, 100)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 监听 tableData 变化,清除单选选中状态
|
||||||
|
watch(
|
||||||
|
() => props.tableData,
|
||||||
|
() => {
|
||||||
|
if (props.singleSelect) {
|
||||||
|
selectedRadioRow.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 监听 tableData 变化,清除单选选中状态
|
||||||
|
watch(
|
||||||
|
() => props.tableData,
|
||||||
|
() => {
|
||||||
|
if (props.singleSelect) {
|
||||||
|
selectedRadioRow.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// 处理勾选变化
|
// 处理勾选变化
|
||||||
const handleSelectionChange = (rows: any[]) => {
|
const handleSelectionChange = (rows: any[]) => {
|
||||||
emit('selection-change', rows)
|
emit('selection-change', rows)
|
||||||
|
|
@ -409,6 +458,15 @@ const handleRowContextmenu = (row: any, column: any, event: MouseEvent) => {
|
||||||
emit('row-contextmenu', row, column, event)
|
emit('row-contextmenu', row, column, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 监听 tableData 变化,清除单选选中状态
|
||||||
|
watch(
|
||||||
|
() => props.tableData,
|
||||||
|
() => {
|
||||||
|
if (props.singleSelect) {
|
||||||
|
selectedRadioRow.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
// 监听 tableData 变化
|
// 监听 tableData 变化
|
||||||
watch(
|
watch(
|
||||||
() => props.tableData,
|
() => props.tableData,
|
||||||
|
|
|
||||||
|
|
@ -101,9 +101,11 @@
|
||||||
<Loading />
|
<Loading />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="!qrcodeLoading">
|
<div v-show="!qrcodeLoading" class="qrcode-list">
|
||||||
<img v-if="qrcodeImageUrl" :src="qrcodeImageUrl" alt="支付二维码" class="qrcode-image" />
|
<div v-for="(url, index) in paymentUrls" :key="index" class="qrcode-item">
|
||||||
<div v-else ref="qrcodeRef" class="qrcode"></div>
|
<div class="qrcode-label">支付链接 {{ index + 1 }}</div>
|
||||||
|
<img :src="qrcodeImageUrls[index]" alt="支付二维码" class="qrcode-image" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="qrcode-tip">请使用{{ getMethodName(selectedMethod) }}扫描上方二维码完成支付</div>
|
<div class="qrcode-tip">请使用{{ getMethodName(selectedMethod) }}扫描上方二维码完成支付</div>
|
||||||
|
|
@ -277,6 +279,12 @@ const qrcodeLoading = ref(false)
|
||||||
// 二维码图片URL
|
// 二维码图片URL
|
||||||
const qrcodeImageUrl = ref('')
|
const qrcodeImageUrl = ref('')
|
||||||
|
|
||||||
|
// 多个二维码图片URL列表
|
||||||
|
const qrcodeImageUrls = ref<string[]>([])
|
||||||
|
|
||||||
|
// 支付链接列表
|
||||||
|
const paymentUrls = ref<string[]>([])
|
||||||
|
|
||||||
// 生成二维码
|
// 生成二维码
|
||||||
const generateQRCode = async (url: string) => {
|
const generateQRCode = async (url: string) => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
@ -286,7 +294,7 @@ const generateQRCode = async (url: string) => {
|
||||||
width: 200,
|
width: 200,
|
||||||
margin: 2,
|
margin: 2,
|
||||||
})
|
})
|
||||||
qrcodeImageUrl.value = dataUrl
|
qrcodeImageUrl.value = dataUrl
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('生成二维码失败:', error)
|
console.error('生成二维码失败:', error)
|
||||||
ElMessage.error('生成二维码失败')
|
ElMessage.error('生成二维码失败')
|
||||||
|
|
@ -296,6 +304,23 @@ const generateQRCode = async (url: string) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 批量生成二维码
|
||||||
|
const generateQRCodes = async (urls: string[]) => {
|
||||||
|
// 直接生成所有二维码图片
|
||||||
|
for (let i = 0; i < urls.length; i++) {
|
||||||
|
try {
|
||||||
|
const dataUrl = await QRCode.toDataURL(urls[i] as any, {
|
||||||
|
width: 200,
|
||||||
|
margin: 2,
|
||||||
|
})
|
||||||
|
qrcodeImageUrls.value[i] = dataUrl as any
|
||||||
|
} catch (error) {
|
||||||
|
console.error('生成二维码失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qrcodeLoading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
// 弹窗基础状态
|
// 弹窗基础状态
|
||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
const rechangeFormRef = ref<FormInstance>()
|
const rechangeFormRef = ref<FormInstance>()
|
||||||
|
|
@ -458,10 +483,13 @@ const handleCreateOrder = async () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// 获取跳转URL并打开支付页面
|
// 获取跳转URL并打开支付页面
|
||||||
const payUrl = res.payment_urls[1]
|
const payUrls = res.payment_urls || []
|
||||||
if (payUrl) {
|
if (payUrls.length > 0) {
|
||||||
|
paymentUrls.value = payUrls
|
||||||
if (isMobileDevice.value) {
|
if (isMobileDevice.value) {
|
||||||
// 移动端:直接跳转支付页面
|
// 移动端:优先跳 safe.paylinktrust.com 域名,没有则跳第一个
|
||||||
|
const safeUrl = payUrls.find((url:string)=> url.includes('safe.paylinktrust.com'))
|
||||||
|
const payUrl = safeUrl || payUrls[0]
|
||||||
ElMessage.success('正在跳转支付页面...')
|
ElMessage.success('正在跳转支付页面...')
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = payUrl
|
window.location.href = payUrl
|
||||||
|
|
@ -469,9 +497,9 @@ const handleCreateOrder = async () => {
|
||||||
handleCancel()
|
handleCancel()
|
||||||
}, 500)
|
}, 500)
|
||||||
} else {
|
} else {
|
||||||
// 桌面端:生成二维码
|
// 桌面端:生成所有链接的二维码
|
||||||
ElMessage.success('正在生成支付二维码...')
|
ElMessage.success('正在生成支付二维码...')
|
||||||
await generateQRCode(payUrl)
|
await generateQRCodes(payUrls)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ElMessage.error('获取支付链接失败')
|
ElMessage.error('获取支付链接失败')
|
||||||
|
|
@ -543,6 +571,8 @@ const handleCancel = () => {
|
||||||
rechangeFormRef.value?.resetFields()
|
rechangeFormRef.value?.resetFields()
|
||||||
rechangeForm.value = createDefaultForm()
|
rechangeForm.value = createDefaultForm()
|
||||||
qrcodeImageUrl.value = ''
|
qrcodeImageUrl.value = ''
|
||||||
|
qrcodeImageUrls.value = []
|
||||||
|
paymentUrls.value = []
|
||||||
qrcodeLoading.value = false
|
qrcodeLoading.value = false
|
||||||
payStep.value = 1
|
payStep.value = 1
|
||||||
selectedMethod.value = ''
|
selectedMethod.value = ''
|
||||||
|
|
@ -800,6 +830,26 @@ onMounted(() => {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.qrcode-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
.qrcode {
|
.qrcode {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,6 @@
|
||||||
@reset="handleReset"
|
@reset="handleReset"
|
||||||
>
|
>
|
||||||
<template #button>
|
<template #button>
|
||||||
<!-- 审核动作先保留按钮交互,后续可直接接接口 -->
|
|
||||||
<el-button type="danger" plain @click="handleReject" v-auth="'user_reviewRechargeOrder'"
|
|
||||||
>驳回</el-button
|
|
||||||
>
|
|
||||||
<el-button type="success" @click="handleApprove" v-auth="'user_reviewRechargeOrder'"
|
|
||||||
>通过</el-button
|
|
||||||
>
|
|
||||||
<el-button type="primary" plain @click="handleExport">导出</el-button>
|
<el-button type="primary" plain @click="handleExport">导出</el-button>
|
||||||
</template>
|
</template>
|
||||||
</RechangeSearch>
|
</RechangeSearch>
|
||||||
|
|
@ -27,9 +20,21 @@
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:show-selection="true"
|
:show-selection="false"
|
||||||
|
:single-select="false"
|
||||||
@selection-change="handleSelectionChange"
|
@selection-change="handleSelectionChange"
|
||||||
/>
|
>
|
||||||
|
<!-- 操作列:驳回/通过按钮 -->
|
||||||
|
<template #action="{ row }">
|
||||||
|
<el-button type="danger" text size="small" @click="handleReject(row)" v-auth="'user_reviewRechargeOrder'">
|
||||||
|
驳回
|
||||||
|
</el-button>
|
||||||
|
<span style="color: #dcdfe6; margin: 0 2px;">|</span>
|
||||||
|
<el-button type="success" text size="small" @click="handleApprove(row)" v-auth="'user_reviewRechargeOrder'">
|
||||||
|
通过
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</Table>
|
||||||
|
|
||||||
<!-- 分页区域:使用接口返回的 total / current_page / per_page -->
|
<!-- 分页区域:使用接口返回的 total / current_page / per_page -->
|
||||||
<RechangePagination
|
<RechangePagination
|
||||||
|
|
@ -198,10 +203,10 @@ console.log('rows:', rows);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 审核通过:当前保留前端提示,等待后续接接口。
|
// 审核通过:支持从表格操作列传入当前行,或保持原有逻辑从选中行获取。
|
||||||
const handleApprove = async () => {
|
const handleApprove = async (row?: any) => {
|
||||||
console.log('selectedRows.value:', selectedRows.value);
|
const targetRow = row || selectedRows.value[0]
|
||||||
if (!selectedRows.value.length) {
|
if (!targetRow) {
|
||||||
ElMessage.warning('请先选择要通过的记录')
|
ElMessage.warning('请先选择要通过的记录')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -217,21 +222,22 @@ const handleApprove = async () => {
|
||||||
|
|
||||||
startLoading()
|
startLoading()
|
||||||
try {
|
try {
|
||||||
await reviewRechargeOrder({ id: selectedRows.value[0]?.id, status: '3' })
|
await reviewRechargeOrder({ id: targetRow.id, status: '3' })
|
||||||
await getList()
|
await getList()
|
||||||
} finally {
|
} finally {
|
||||||
stopLoading()
|
stopLoading()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 审核驳回:当前保留前端提示,等待后续接接口。
|
// 审核驳回:支持从表格操作列传入当前行,或保持原有逻辑从选中行获取。
|
||||||
const handleReject = async () => {
|
const handleReject = async (row?: any) => {
|
||||||
if (!selectedRows.value.length) {
|
const targetRow = row || selectedRows.value[0]
|
||||||
|
if (!targetRow) {
|
||||||
ElMessage.warning('请先选择要驳回的记录')
|
ElMessage.warning('请先选择要驳回的记录')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm(`是否确认驳回该审核?订单号:${selectedRows.value[0]?.orderNo}`, '审核确认', {
|
await ElMessageBox.confirm(`是否确认驳回该审核?订单号:${targetRow.orderNo}`, '审核确认', {
|
||||||
confirmButtonText: '确认',
|
confirmButtonText: '确认',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
|
|
@ -243,7 +249,7 @@ const handleReject = async () => {
|
||||||
//充值状态:1未审核,2未付款,3已付款,4驳回
|
//充值状态:1未审核,2未付款,3已付款,4驳回
|
||||||
startLoading()
|
startLoading()
|
||||||
try {
|
try {
|
||||||
await reviewRechargeOrder({ id: selectedRows.value[0]?.id, status: '4' })
|
await reviewRechargeOrder({ id: targetRow.id, status: '4' })
|
||||||
await getList()
|
await getList()
|
||||||
} finally {
|
} finally {
|
||||||
stopLoading()
|
stopLoading()
|
||||||
|
|
@ -269,7 +275,7 @@ const handlePaginationChange = (page: { currentPage: number; pageSize: number })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.rechange-check {
|
.rechange-check {
|
||||||
|
|
||||||
// 覆盖公共搜索组件默认的弹性拉伸,让当前页搜索项宽度按配置展示。
|
// 覆盖公共搜索组件默认的弹性拉伸,让当前页搜索项宽度按配置展示。
|
||||||
:deep(.search-container .el-form) {
|
:deep(.search-container .el-form) {
|
||||||
|
|
|
||||||
|
|
@ -47,4 +47,5 @@ export const rechangeCheckTableColumns = [
|
||||||
{ prop: 'rate', label: '汇率', align: 'center' as const, width: 100 },
|
{ prop: 'rate', label: '汇率', align: 'center' as const, width: 100 },
|
||||||
{ prop: 'statusText', label: '状态', align: 'center' as const, width: 100 },
|
{ prop: 'statusText', label: '状态', align: 'center' as const, width: 100 },
|
||||||
{ prop: 'rechangeTime', label: '充值时间', align: 'center' as const },
|
{ prop: 'rechangeTime', label: '充值时间', align: 'center' as const },
|
||||||
|
{ label: '操作', align: 'center' as const, width: 180, fixed: 'right' as const, slotName: 'action' },
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<el-button @click="handleBack">{{ t('common.cancel') }}</el-button>
|
<el-button @click="handleBack">{{ t('common.cancel') }}</el-button>
|
||||||
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">{{ t('withdraw.dialog.confirmWithdraw')
|
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">{{ t('withdraw.dialog.confirmWithdraw')
|
||||||
}}</el-button>
|
}}</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
@ -238,36 +238,36 @@ const availableBalance = computed(() => {
|
||||||
Math.max(0, parseFloat(walletInfo.value.risk || '0')) +
|
Math.max(0, parseFloat(walletInfo.value.risk || '0')) +
|
||||||
Math.max(0, parseFloat(walletInfo.value.service || '0')) +
|
Math.max(0, parseFloat(walletInfo.value.service || '0')) +
|
||||||
Math.max(0, parseFloat(walletInfo.value.commission_fee_handling || '0')) +
|
Math.max(0, parseFloat(walletInfo.value.commission_fee_handling || '0')) +
|
||||||
Math.max(0, parseFloat(walletInfo.value.commission_point_diff || '0'))
|
Math.max(0, parseFloat(walletInfo.value.commission_point_diff || '0'))
|
||||||
// Math.max(0, parseFloat(walletInfo.value.closing_profit || '0'))
|
// Math.max(0, parseFloat(walletInfo.value.closing_profit || '0'))
|
||||||
return floorMoney(amount - walletSum)
|
return floorMoney(amount - walletSum)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 计算钱包余额总和(用于验证输入金额上限)
|
// 计算钱包余额总和(用于验证输入金额上限)
|
||||||
// roleId >= 5 时,totalWalletBalance 需要包含 availableBalance
|
// roleId >= 5 时,totalWalletBalance 需要包含 availableBalance
|
||||||
const totalWalletBalance = computed(() => {
|
const totalWalletBalance = computed(() => {
|
||||||
const amount = parseFloat(walletInfo.value.amount || '0')
|
const amount = parseFloat(walletInfo.value.amount || '0')
|
||||||
const freeze = parseFloat(walletInfo.value.freeze || '0')
|
const freeze = parseFloat(walletInfo.value.freeze || '0')
|
||||||
console.log('walletInfo.value:', amount , freeze);
|
console.log('walletInfo.value:', amount, freeze);
|
||||||
|
|
||||||
// 钱包余额总和(手续费 + 点差 + 服务费 + 风险金 + 盈亏)
|
// 钱包余额总和(手续费 + 点差 + 服务费 + 风险金 + 盈亏)
|
||||||
// const walletSum =
|
const walletSum =
|
||||||
// parseFloat(walletInfo.value.commission_fee_handling || '0') +
|
parseFloat(walletInfo.value.commission_fee_handling || '0') +
|
||||||
// parseFloat(walletInfo.value.commission_point_diff || '0') +
|
parseFloat(walletInfo.value.commission_point_diff || '0') +
|
||||||
// parseFloat(walletInfo.value.service || '0') +
|
parseFloat(walletInfo.value.service || '0') +
|
||||||
// parseFloat(walletInfo.value.risk || '0') +
|
parseFloat(walletInfo.value.risk || '0') +
|
||||||
// parseFloat(walletInfo.value.closing_profit || '0')
|
parseFloat(Math.min(0, +walletInfo.value.closing_profit)+'')
|
||||||
|
|
||||||
// if (roleId >= 5) {
|
if (roleId >= 5) {
|
||||||
// // roleId >= 5 时,总余额包含可用余额
|
// roleId >= 5 时,总余额包含可用余额
|
||||||
// // availableBalance = amount - freeze - walletSum
|
// availableBalance = amount - freeze - walletSum
|
||||||
// const availableBal = Math.max(0, amount - walletSum)
|
// const availableBal = Math.max(0, amount - walletSum)
|
||||||
// return walletSum + availableBal
|
return floorMoney(amount - freeze)
|
||||||
// }
|
}
|
||||||
return floorMoney(amount - freeze)
|
return floorMoney(walletSum)
|
||||||
})
|
})
|
||||||
|
|
||||||
const floorMoney = (num:number) => {
|
const floorMoney = (num: number) => {
|
||||||
const cents = Math.floor(num * 100);
|
const cents = Math.floor(num * 100);
|
||||||
return cents / 100;
|
return cents / 100;
|
||||||
}
|
}
|
||||||
|
|
@ -295,10 +295,10 @@ const feeFieldMap: Record<string, keyof WalletInfo | 'available'> = {
|
||||||
// 费用项顺序
|
// 费用项顺序
|
||||||
const feeOrder = computed(() => {
|
const feeOrder = computed(() => {
|
||||||
const orders: Record<number, string[]> = {
|
const orders: Record<number, string[]> = {
|
||||||
1: ['手续费', '点差', '服务费', '风险金', '盈亏', '可用余额'], // 平台
|
1: ['手续费', '点差', '服务费', '风险金', '盈亏'], // 平台
|
||||||
2: ['手续费', '点差', '服务费', '可用余额'], // 承包商
|
2: ['手续费', '点差', '服务费'], // 承包商
|
||||||
3: ['手续费', '点差', '服务费', '盈亏', '可用余额'], // 特殊做市商
|
3: ['手续费', '点差', '服务费', '盈亏'], // 特殊做市商
|
||||||
4: ['手续费', '点差', '服务费', '盈亏', '可用余额'], // 普通做市商
|
4: ['手续费', '点差', '服务费', '盈亏'], // 普通做市商
|
||||||
5: ['手续费', '点差', '可用余额'], // 代理
|
5: ['手续费', '点差', '可用余额'], // 代理
|
||||||
6: ['手续费', '点差', '可用余额'], // 用户
|
6: ['手续费', '点差', '可用余额'], // 用户
|
||||||
}
|
}
|
||||||
|
|
@ -308,25 +308,25 @@ const feeOrder = computed(() => {
|
||||||
// 根据角色动态计算钱包项
|
// 根据角色动态计算钱包项
|
||||||
const walletItems = computed(() => {
|
const walletItems = computed(() => {
|
||||||
const items: { label: string; value: string; field: string }[] = []
|
const items: { label: string; value: string; field: string }[] = []
|
||||||
|
|
||||||
for (const feeType of feeOrder.value) {
|
for (const feeType of feeOrder.value) {
|
||||||
const field = feeFieldMap[feeType]
|
const field = feeFieldMap[feeType]
|
||||||
if (!field) continue
|
if (!field) continue
|
||||||
|
|
||||||
let value: string
|
let value: string
|
||||||
if (field === 'available') {
|
if (field === 'available') {
|
||||||
value = availableBalance.value.toFixed(2)
|
value = availableBalance.value.toFixed(2)
|
||||||
} else {
|
} else {
|
||||||
value = walletInfo.value[field] || '0.00'
|
value = walletInfo.value[field] || '0.00'
|
||||||
}
|
}
|
||||||
|
|
||||||
items.push({
|
items.push({
|
||||||
label: feeLabelMap[feeType] || feeType,
|
label: feeLabelMap[feeType] || feeType,
|
||||||
value,
|
value,
|
||||||
field,
|
field,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return items
|
return items
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -413,7 +413,7 @@ const initData = async () => {
|
||||||
if (res) {
|
if (res) {
|
||||||
walletInfo.value = {
|
walletInfo.value = {
|
||||||
commission_fee_handling: res.commission_fee_handling || '0',
|
commission_fee_handling: res.commission_fee_handling || '0',
|
||||||
commission_point_diff: ''+floorMoney(res.commission_point_diff - res.freeze||0) || '0',
|
commission_point_diff: '' + floorMoney(res.commission_point_diff - res.freeze || 0) || '0',
|
||||||
service: res.service || '0',
|
service: res.service || '0',
|
||||||
risk: res.risk || '0',
|
risk: res.risk || '0',
|
||||||
closing_profit: res.closing_profit || '0',
|
closing_profit: res.closing_profit || '0',
|
||||||
|
|
@ -542,7 +542,7 @@ const handleNextStep = () => {
|
||||||
// 根据当前角色的费用项顺序填充表单数据
|
// 根据当前角色的费用项顺序填充表单数据
|
||||||
for (const feeType of feeOrder.value) {
|
for (const feeType of feeOrder.value) {
|
||||||
const deducted = calculatedData.value.find(item => item.type === feeType)?.deducted || '0'
|
const deducted = calculatedData.value.find(item => item.type === feeType)?.deducted || '0'
|
||||||
|
|
||||||
switch (feeType) {
|
switch (feeType) {
|
||||||
case '手续费':
|
case '手续费':
|
||||||
formData.value.withdraw_fee_handle = deducted
|
formData.value.withdraw_fee_handle = deducted
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="withdraw-check table_form-container">
|
<div class="withdraw-check table_form-container">
|
||||||
<!-- 搜索区域:复用项目内搜索组件 -->
|
<!-- 搜索区域:复用项目内搜索组件 -->
|
||||||
<WithdrawSearch :search-form="searchForm" :search-options="searchOptions" @search="handleSearch" @reset="handleReset">
|
<WithdrawSearch :search-form="searchForm" :search-options="searchOptions" @search="handleSearch" @reset="handleReset" />
|
||||||
<template #button>
|
|
||||||
<el-button type="success" :loading="approveLoading" @click="handleApprove">通过</el-button>
|
|
||||||
<el-button type="danger" plain :loading="rejectLoading" @click="handleReject">驳回</el-button>
|
|
||||||
</template>
|
|
||||||
</WithdrawSearch>
|
|
||||||
|
|
||||||
<!-- 列表区域:使用公共 Table 组件 -->
|
<!-- 列表区域:使用公共 Table 组件 -->
|
||||||
<Table
|
<Table
|
||||||
|
|
@ -17,7 +12,18 @@
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:show-selection="true"
|
:show-selection="true"
|
||||||
@selection-change="handleSelectionChange"
|
@selection-change="handleSelectionChange"
|
||||||
/>
|
>
|
||||||
|
<!-- 操作列:驳回/通过按钮 -->
|
||||||
|
<template #action="{ row }">
|
||||||
|
<el-button type="success" text size="small" @click="handleApprove(row)" v-auth="'user_reviewWithdrawOrder'">
|
||||||
|
通过
|
||||||
|
</el-button>
|
||||||
|
<span style="color: #dcdfe6; margin: 0 2px;">|</span>
|
||||||
|
<el-button type="danger" text size="small" @click="handleReject(row)" v-auth="'user_reviewWithdrawOrder'">
|
||||||
|
驳回
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</Table>
|
||||||
|
|
||||||
<!-- 分页区域:复用项目内分页组件 -->
|
<!-- 分页区域:复用项目内分页组件 -->
|
||||||
<WithdrawPagination v-model="currentPage" v-model:pageSizeValue="pageSize" :total="total"
|
<WithdrawPagination v-model="currentPage" v-model:pageSizeValue="pageSize" :total="total"
|
||||||
|
|
@ -216,16 +222,17 @@ const handleSelectionChange = (rows: TableRow[]) => {
|
||||||
selectedRows.value = rows
|
selectedRows.value = rows
|
||||||
}
|
}
|
||||||
|
|
||||||
// 审核通过
|
// 审核通过:支持从表格操作列传入当前行,或保持原有批量操作逻辑
|
||||||
const handleApprove = async () => {
|
const handleApprove = async (row?: TableRow) => {
|
||||||
if (!selectedRows.value.length) {
|
const rowsToProcess = row ? [row] : selectedRows.value
|
||||||
|
if (!rowsToProcess.length) {
|
||||||
ElMessage.warning('请先选择要通过的记录')
|
ElMessage.warning('请先选择要通过的记录')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm(
|
await ElMessageBox.confirm(
|
||||||
`确定要通过 ${selectedRows.value.length} 条提现记录吗?`,
|
`确定要通过 ${rowsToProcess.length} 条提现记录吗?`,
|
||||||
'审核确认',
|
'审核确认',
|
||||||
{
|
{
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
|
|
@ -236,9 +243,9 @@ const handleApprove = async () => {
|
||||||
|
|
||||||
approveLoading.value = true
|
approveLoading.value = true
|
||||||
|
|
||||||
for (const row of selectedRows.value) {
|
for (const item of rowsToProcess) {
|
||||||
await withdrawReviewOrder({
|
await withdrawReviewOrder({
|
||||||
id: row.id,
|
id: item.id,
|
||||||
status: 2
|
status: 2
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -254,16 +261,17 @@ const handleApprove = async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 审核驳回
|
// 审核驳回:支持从表格操作列传入当前行,或保持原有批量操作逻辑
|
||||||
const handleReject = async () => {
|
const handleReject = async (row?: TableRow) => {
|
||||||
if (!selectedRows.value.length) {
|
const rowsToProcess = row ? [row] : selectedRows.value
|
||||||
|
if (!rowsToProcess.length) {
|
||||||
ElMessage.warning('请先选择要驳回的记录')
|
ElMessage.warning('请先选择要驳回的记录')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm(
|
await ElMessageBox.confirm(
|
||||||
`确定要驳回 ${selectedRows.value.length} 条提现记录吗?`,
|
`确定要驳回 ${rowsToProcess.length} 条提现记录吗?`,
|
||||||
'审核确认',
|
'审核确认',
|
||||||
{
|
{
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
|
|
@ -274,9 +282,9 @@ const handleReject = async () => {
|
||||||
|
|
||||||
rejectLoading.value = true
|
rejectLoading.value = true
|
||||||
|
|
||||||
for (const row of selectedRows.value) {
|
for (const item of rowsToProcess) {
|
||||||
await withdrawReviewOrder({
|
await withdrawReviewOrder({
|
||||||
id: row.id,
|
id: item.id,
|
||||||
status: 4
|
status: 4
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ export const withdrawCheckTableColumns = [
|
||||||
{ prop: 'withdraw_point_diff', label: '点差', align: 'center' as const, width: 100, isNumber: true },
|
{ prop: 'withdraw_point_diff', label: '点差', align: 'center' as const, width: 100, isNumber: true },
|
||||||
{ prop: 'withdraw_risk_fee', label: '风控费', align: 'center' as const, isNumber: true },
|
{ prop: 'withdraw_risk_fee', label: '风控费', align: 'center' as const, isNumber: true },
|
||||||
{ prop: 'withdraw_address', label: '提现地址', align: 'center' as const},
|
{ prop: 'withdraw_address', label: '提现地址', align: 'center' as const},
|
||||||
{ prop: 'created_at', label: '申请时间', align: 'center' as const}
|
{ prop: 'created_at', label: '申请时间', align: 'center' as const},
|
||||||
|
{ label: '操作', align: 'center' as const, width: 140, fixed: 'right' as const, slotName: 'action' },
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -69,14 +69,14 @@
|
||||||
<div class="balance-section">
|
<div class="balance-section">
|
||||||
<div class="balance-label">总金额</div>
|
<div class="balance-label">总金额</div>
|
||||||
<div class="balance-value">
|
<div class="balance-value">
|
||||||
<AnimatedNumber :value="walletCapital.amount_total || 0" :decimals="5" />
|
<AnimatedNumber :value="walletCapital.amount_total || 0" :decimals="2" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="balance-detail">
|
<div class="balance-detail">
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<span class="label">可用资金</span>
|
<span class="label">可用资金</span>
|
||||||
<span class="value">
|
<span class="value">
|
||||||
<AnimatedNumber :value="walletCapital.amount || 0" :decimals="5" />
|
<AnimatedNumber :value="walletCapital.amount || 0" :decimals="2" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="isAgent">
|
<template v-if="isAgent">
|
||||||
|
|
@ -152,13 +152,13 @@
|
||||||
<div class="stat-item">
|
<div class="stat-item">
|
||||||
<span class="stat-label">净资产</span>
|
<span class="stat-label">净资产</span>
|
||||||
<span class="stat-value">
|
<span class="stat-value">
|
||||||
<AnimatedNumber :value="item?.wallets_trade?.amount || 0" :decimals="5" />
|
<AnimatedNumber :value="item?.wallets_trade?.amount || 0" :decimals="2" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-item">
|
<div class="stat-item">
|
||||||
<span class="stat-label">余额</span>
|
<span class="stat-label">余额</span>
|
||||||
<span class="stat-value">
|
<span class="stat-value">
|
||||||
<AnimatedNumber :value="item?.wallets_trade?.amount || 0" :decimals="5" />
|
<AnimatedNumber :value="item?.wallets_trade?.amount || 0" :decimals="2" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-item">
|
<div class="stat-item">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue