campus-errand/admin/src/views/Withdrawals.vue
2026-03-20 18:09:42 +08:00

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>