liujie
2026-08-21 ad5db85e540947e82c7a4d5612879b3b3ecdf459
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
<script setup>
import { onMounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { Refresh, Search } from '@element-plus/icons-vue'
import { getWithdrawals, processWithdrawal } from '@/api/finance'
 
const loading = ref(false)
const processing = ref(false)
const records = ref([])
const total = ref(0)
const dateRange = ref([])
const dialogVisible = ref(false)
const current = ref(null)
const query = reactive({ page: 1, size: 10, keyword: '', status: '' })
const processForm = reactive({ status: 'APPROVED', auditRemark: '' })
const statusMap = { PENDING: { label: '未处理', type: 'primary' }, TRANSFERRING: { label: '划账处理中', type: 'warning' }, TRANSFERRED: { label: '待发起取现', type: 'warning' }, PROCESSING: { label: '到账处理中', type: 'warning' }, APPROVED: { label: '已到账', type: 'success' }, REJECTED: { label: '已驳回', type: 'danger' } }
const formatMoney = (value) => Number(value || 0).toFixed(2)
const formatDateTime = (value) => value ? value.replace('T', ' ').slice(0, 19) : '-'
 
const loadWithdrawals = async () => {
  loading.value = true
  try {
    const [startDate, endDate] = dateRange.value || []
    const result = await getWithdrawals({ page: query.page, size: query.size, keyword: query.keyword.trim() || undefined, status: query.status || undefined, startDate, endDate })
    records.value = result.records
    total.value = result.total
  } finally { loading.value = false }
}
const search = () => { query.page = 1; loadWithdrawals() }
const reset = () => { Object.assign(query, { page: 1, size: 10, keyword: '', status: '' }); dateRange.value = []; loadWithdrawals() }
const changePage = (page) => { query.page = page; loadWithdrawals() }
const changePageSize = (size) => { query.page = 1; query.size = size; loadWithdrawals() }
const openProcess = (row) => { current.value = row; Object.assign(processForm, { status: 'APPROVED', auditRemark: row.auditRemark || '' }); dialogVisible.value = true }
const submitProcess = async () => {
  if (processForm.status === 'REJECTED' && !processForm.auditRemark.trim()) { ElMessage.warning('驳回提现时请填写处理备注'); return }
  processing.value = true
  try {
    await processWithdrawal(current.value.id, { status: processForm.status, auditRemark: processForm.auditRemark.trim() || undefined })
    ElMessage.success(processForm.status === 'APPROVED' ? '提现申请已受理,等待到账结果' : '提现申请已驳回')
    dialogVisible.value = false
    loadWithdrawals()
  } finally { processing.value = false }
}
onMounted(loadWithdrawals)
</script>
 
<template>
  <section class="withdrawal-page">
    <el-card class="withdrawal-card" shadow="never">
      <h2>查询表格</h2>
      <el-form class="query-form" label-position="left" label-width="70px" @submit.prevent="search">
        <el-form-item label="导游搜索"><el-input v-model="query.keyword" clearable placeholder="请输入导游手机号/导游姓名/提现编号" @keyup.enter="search" /></el-form-item>
        <el-form-item label="申请时间"><el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" /></el-form-item>
        <el-form-item label="状态"><el-select v-model="query.status" clearable placeholder="全部"><el-option label="未处理" value="PENDING" /><el-option label="划账处理中" value="TRANSFERRING" /><el-option label="待发起取现" value="TRANSFERRED" /><el-option label="到账处理中" value="PROCESSING" /><el-option label="已到账" value="APPROVED" /><el-option label="已驳回" value="REJECTED" /></el-select></el-form-item>
        <div class="query-actions"><el-button type="primary" :icon="Search" @click="search">查询</el-button><el-button :icon="Refresh" @click="reset">重置</el-button></div>
      </el-form>
      <el-table v-loading="loading" :data="records" border row-key="id">
        <el-table-column prop="withdrawalNo" label="提现编号" min-width="145" align="center" />
        <el-table-column prop="guideName" label="导游名称" min-width="120" align="center" />
        <el-table-column prop="guideMobile" label="导游手机号" min-width="140" align="center" />
        <el-table-column label="申请时间" min-width="170" align="center"><template #default="{ row }">{{ formatDateTime(row.appliedAt) }}</template></el-table-column>
        <el-table-column label="提现金额" min-width="120" align="center"><template #default="{ row }">{{ formatMoney(row.amount) }}</template></el-table-column>
        <el-table-column label="状态" width="110" align="center"><template #default="{ row }"><el-tag :type="statusMap[row.status]?.type || 'info'" effect="plain">{{ statusMap[row.status]?.label || row.status }}</el-tag></template></el-table-column>
        <el-table-column label="操作" width="110" fixed="right" align="center"><template #default="{ row }"><el-button link type="primary" @click="openProcess(row)">{{ row.status === 'PENDING' ? '处理' : '查看' }}</el-button></template></el-table-column>
        <template #empty><el-empty description="暂无提现申请" /></template>
      </el-table>
      <div class="pagination-row"><span>共 {{ total }} 条</span><el-pagination background layout="sizes, prev, pager, next, jumper" :current-page="query.page" :page-size="query.size" :page-sizes="[10, 20, 50]" :total="total" @current-change="changePage" @size-change="changePageSize" /></div>
    </el-card>
    <el-dialog v-model="dialogVisible" :title="current?.status === 'PENDING' ? '处理提现申请' : '提现申请详情'" width="560px">
      <el-descriptions v-if="current" :column="2" border>
        <el-descriptions-item label="提现编号">{{ current.withdrawalNo }}</el-descriptions-item><el-descriptions-item label="导游名称">{{ current.guideName }}</el-descriptions-item>
        <el-descriptions-item label="导游手机号">{{ current.guideMobile }}</el-descriptions-item><el-descriptions-item label="提现金额">{{ formatMoney(current.amount) }}</el-descriptions-item>
        <el-descriptions-item label="申请时间" :span="2">{{ formatDateTime(current.appliedAt) }}</el-descriptions-item>
        <el-descriptions-item v-if="current.processor" label="处理人">{{ current.processor }}</el-descriptions-item><el-descriptions-item v-if="current.processedAt" label="处理时间">{{ formatDateTime(current.processedAt) }}</el-descriptions-item>
        <el-descriptions-item v-if="current.transferReqSeqId" label="划账流水" :span="2">{{ current.transferReqSeqId }}</el-descriptions-item>
        <el-descriptions-item v-if="current.cashReqSeqId" label="取现流水" :span="2">{{ current.cashReqSeqId }}</el-descriptions-item>
        <el-descriptions-item v-if="current.payoutMessage" label="资金状态" :span="2">{{ current.payoutMessage }}</el-descriptions-item>
      </el-descriptions>
      <el-form v-if="current?.status === 'PENDING'" class="process-form" label-width="82px">
        <el-form-item label="处理结果"><el-radio-group v-model="processForm.status"><el-radio-button value="APPROVED">通过</el-radio-button><el-radio-button value="REJECTED">驳回</el-radio-button></el-radio-group></el-form-item>
        <el-form-item label="处理备注"><el-input v-model="processForm.auditRemark" type="textarea" :rows="3" maxlength="255" show-word-limit /></el-form-item>
      </el-form>
      <div v-else-if="current?.auditRemark" class="remark-row"><strong>处理备注:</strong>{{ current.auditRemark }}</div>
      <template #footer><el-button @click="dialogVisible = false">关闭</el-button><el-button v-if="current?.status === 'PENDING'" type="primary" :loading="processing" @click="submitProcess">确认处理</el-button></template>
    </el-dialog>
  </section>
</template>
 
<style scoped>
.withdrawal-card { border: 0; border-radius: 4px; }
.withdrawal-card h2 { margin: 0 0 22px; font-size: 17px; }
.query-form { display: grid; grid-template-columns: minmax(290px, 1fr) minmax(340px, 1fr) minmax(220px, .75fr) auto; gap: 18px 28px; margin-bottom: 28px; padding-bottom: 24px; border-bottom: 1px solid #e5e7eb; }
.query-form :deep(.el-form-item) { margin: 0; }
.query-form :deep(.el-select), .query-form :deep(.el-date-editor) { width: 100%; }
.query-actions { display: flex; flex-direction: column; gap: 10px; }
.pagination-row { display: flex; justify-content: flex-end; align-items: center; gap: 18px; margin-top: 22px; }
.pagination-row > span { color: #64748b; font-size: 13px; }
.process-form { margin-top: 20px; }
.remark-row { margin-top: 18px; padding: 14px; color: #475467; background: #f7f8fa; }
</style>