127 lines
4.7 KiB
Vue
127 lines
4.7 KiB
Vue
<template>
|
|
<div class="withdrawals-page">
|
|
<div class="page-header">
|
|
<h2>提现管理</h2>
|
|
<el-radio-group v-model="statusFilter" @change="loadData">
|
|
<el-radio-button label="">全部</el-radio-button>
|
|
<el-radio-button label="Pending">待处理</el-radio-button>
|
|
<el-radio-button label="Processing">处理中</el-radio-button>
|
|
<el-radio-button label="Completed">已完成</el-radio-button>
|
|
</el-radio-group>
|
|
</div>
|
|
|
|
<el-table :data="list" stripe v-loading="loading" style="width: 100%">
|
|
<el-table-column prop="id" label="ID" width="70" />
|
|
<el-table-column prop="userNickname" label="用户" min-width="100" show-overflow-tooltip />
|
|
<el-table-column prop="amount" label="金额" width="100">
|
|
<template #default="{ row }">
|
|
<span style="color: #e64340; font-weight: bold">¥{{ row.amount }}</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="paymentMethod" label="收款方式" width="100">
|
|
<template #default="{ row }">
|
|
<el-tag :type="row.paymentMethod === 'WeChat' ? 'success' : 'primary'" round size="small">
|
|
{{ row.paymentMethod === 'WeChat' ? '微信' : '支付宝' }}
|
|
</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="收款码" width="80">
|
|
<template #default="{ row }">
|
|
<el-image v-if="row.qrCodeImage" :src="row.qrCodeImage" :preview-src-list="[row.qrCodeImage]"
|
|
style="width: 40px; height: 40px" fit="cover" preview-teleported />
|
|
<span v-else>-</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="status" label="状态" width="100">
|
|
<template #default="{ row }">
|
|
<el-tag :type="statusTagType(row.status)" round size="small">{{ statusLabel(row.status) }}</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="createdAt" label="申请时间" min-width="160">
|
|
<template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
|
|
</el-table-column>
|
|
<el-table-column label="操作" width="220" fixed="right">
|
|
<template #default="{ row }">
|
|
<template v-if="row.status === 'Pending'">
|
|
<el-button type="warning" size="small" plain @click="handleAction(row, 'processing')">处理中</el-button>
|
|
<el-button type="success" size="small" plain @click="handleAction(row, 'approve')">通过</el-button>
|
|
<el-button type="danger" size="small" plain @click="handleAction(row, 'reject')">拒绝</el-button>
|
|
</template>
|
|
<template v-else-if="row.status === 'Processing'">
|
|
<el-button type="success" size="small" plain @click="handleAction(row, 'approve')">通过</el-button>
|
|
<el-button type="danger" size="small" plain @click="handleAction(row, 'reject')">拒绝</el-button>
|
|
</template>
|
|
<template v-else>
|
|
<span style="color: #999">已处理</span>
|
|
</template>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from 'vue'
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
import request from '../utils/request'
|
|
|
|
const list = ref([])
|
|
const loading = ref(false)
|
|
const statusFilter = ref('')
|
|
|
|
async function loadData() {
|
|
loading.value = true
|
|
try {
|
|
const params = statusFilter.value ? `?status=${statusFilter.value}` : ''
|
|
const res = await request.get(`/admin/withdrawals${params}`)
|
|
list.value = res || []
|
|
} catch (e) {
|
|
ElMessage.error('加载失败')
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
async function handleAction(row, action) {
|
|
const labels = { approve: '通过', reject: '拒绝', processing: '标记为处理中' }
|
|
try {
|
|
await ElMessageBox.confirm(`确定${labels[action]}该提现申请?`, '提示', { type: 'warning' })
|
|
await request.put(`/admin/withdrawals/${row.id}`, { action })
|
|
ElMessage.success('操作成功')
|
|
loadData()
|
|
} catch (e) {
|
|
if (e !== 'cancel') ElMessage.error(e?.response?.data?.message || '操作失败')
|
|
}
|
|
}
|
|
|
|
function statusLabel(s) {
|
|
return { Pending: '待处理', Processing: '处理中', Completed: '已完成' }[s] || s
|
|
}
|
|
|
|
function statusTagType(s) {
|
|
return { Pending: 'warning', Processing: '', Completed: 'success' }[s] || 'info'
|
|
}
|
|
|
|
function formatTime(str) {
|
|
if (!str) return '-'
|
|
const d = new Date(str)
|
|
const pad = n => String(n).padStart(2, '0')
|
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
|
}
|
|
|
|
onMounted(loadData)
|
|
</script>
|
|
|
|
<style scoped>
|
|
.page-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
.page-header h2 {
|
|
margin: 0;
|
|
font-size: 20px;
|
|
}
|
|
</style>
|