|
|
@@ -0,0 +1,214 @@
|
|
|
+<template>
|
|
|
+ <div class="pending-refund admin-page">
|
|
|
+ <div class="header admin-page-header">
|
|
|
+ <h2 class="admin-page-title">待退款列表</h2>
|
|
|
+ <div class="flex items-center gap-sm">
|
|
|
+ <el-select v-model="statusFilter" placeholder="退款状态" @change="loadList">
|
|
|
+ <el-option label="全部状态" value="" />
|
|
|
+ <el-option label="待处理" value="pending" />
|
|
|
+ <el-option label="处理中" value="processing" />
|
|
|
+ <el-option label="已成功" value="success" />
|
|
|
+ <el-option label="已失败" value="failed" />
|
|
|
+ </el-select>
|
|
|
+ <el-select v-model="typeFilter" placeholder="订单类型" @change="loadList">
|
|
|
+ <el-option label="全部类型" value="" />
|
|
|
+ <el-option label="提现退款" value="withdrawal" />
|
|
|
+ <el-option label="商品退款" value="product" />
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="summary.totalPendingAmount > 0" class="summary-bar">
|
|
|
+ <el-alert
|
|
|
+ :title="'待退款总金额:¥' + (summary.totalPendingAmount / 100).toFixed(2) + '(' + summary.pendingCount + ' 笔待处理)'"
|
|
|
+ type="warning"
|
|
|
+ :closable="false"
|
|
|
+ show-icon
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="table-scroll-wrap">
|
|
|
+ <el-table :data="list" v-loading="loading" border stripe>
|
|
|
+ <el-table-column prop="id" label="ID" width="70" />
|
|
|
+ <el-table-column label="类型" width="100">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <el-tag :type="row.orderType === 'product' ? 'primary' : 'warning'" size="small">
|
|
|
+ {{ row.orderType === 'product' ? '商品退款' : '提现退款' }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="订单号" width="180">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ {{ row.orderNo || '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="提现申请ID" width="100">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ {{ row.withdrawalRequestId || '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="用户ID" width="80">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ {{ row.userId }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="退款金额" width="120">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <span class="amount">{{ formatPriceWithSymbol(row.amount) }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="实退金额" width="120">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <span class="amount">{{ row.refundedAmount ? formatPriceWithSymbol(row.refundedAmount) : '-' }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="状态" width="100">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <el-tag :type="statusType(row.status)">{{ statusLabel(row.status) }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="尝试次数" width="80">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ {{ row.attemptCount }}/{{ row.maxAttempts }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="失败原因" min-width="200" show-overflow-tooltip>
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ {{ row.failureReason || '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="创建时间" width="160">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ {{ formatTime(row.createdAt) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="退款时间" width="160">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ {{ row.refundedAt ? formatTime(row.refundedAt) : '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="120" fixed="right">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <el-button v-if="row.status === 'pending' || row.status === 'processing'" type="danger" size="mini" @click="handleCancel(row)">
|
|
|
+ 中止退款
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="pagination-wrap">
|
|
|
+ <el-pagination
|
|
|
+ background
|
|
|
+ layout="total, prev, pager, next"
|
|
|
+ :total="total"
|
|
|
+ :page-size="pageSize"
|
|
|
+ :current-page.sync="currentPage"
|
|
|
+ @current-change="handlePageChange"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { getPendingRefundList, getPendingRefundSummary, cancelPendingRefund } from '@/api/admin'
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'PendingRefund',
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ list: [],
|
|
|
+ loading: false,
|
|
|
+ statusFilter: '',
|
|
|
+ typeFilter: '',
|
|
|
+ currentPage: 1,
|
|
|
+ pageSize: 20,
|
|
|
+ total: 0,
|
|
|
+ summary: {
|
|
|
+ totalPendingAmount: 0,
|
|
|
+ pendingCount: 0
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.loadSummary()
|
|
|
+ this.loadList()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ async loadSummary() {
|
|
|
+ try {
|
|
|
+ const res = await getPendingRefundSummary()
|
|
|
+ if (res.code === 200) {
|
|
|
+ this.summary = res.data || { totalPendingAmount: 0 }
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error('加载待退款摘要失败', e)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async loadList() {
|
|
|
+ this.loading = true
|
|
|
+ try {
|
|
|
+ const res = await getPendingRefundList({
|
|
|
+ status: this.statusFilter || undefined,
|
|
|
+ page: this.currentPage,
|
|
|
+ size: this.pageSize
|
|
|
+ })
|
|
|
+ if (res.code === 200) {
|
|
|
+ this.list = res.data.records || []
|
|
|
+ this.total = res.data.total || 0
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error('加载待退款列表失败', e)
|
|
|
+ } finally {
|
|
|
+ this.loading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handlePageChange(page) {
|
|
|
+ this.currentPage = page
|
|
|
+ this.loadList()
|
|
|
+ },
|
|
|
+ statusType(status) {
|
|
|
+ const map = { pending: 'warning', processing: 'primary', success: 'success', failed: 'danger', cancelled: 'info' }
|
|
|
+ return map[status] || 'info'
|
|
|
+ },
|
|
|
+ statusLabel(status) {
|
|
|
+ const map = { pending: '待处理', processing: '处理中', success: '已成功', failed: '已失败', cancelled: '已中止' }
|
|
|
+ return map[status] || status
|
|
|
+ },
|
|
|
+ async handleCancel(row) {
|
|
|
+ try {
|
|
|
+ await this.$confirm('确认中止该退款?订单将回到退款审核状态。', '确认中止', { type: 'warning' })
|
|
|
+ const res = await cancelPendingRefund({ refundId: row.id })
|
|
|
+ if (res.code === 200) {
|
|
|
+ this.$message.success('已中止退款')
|
|
|
+ this.loadList()
|
|
|
+ this.loadSummary()
|
|
|
+ } else {
|
|
|
+ this.$message.error(res.message || '操作失败')
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ if (e !== 'cancel') {
|
|
|
+ console.error('中止退款失败', e)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ formatTime(time) {
|
|
|
+ if (!time) return '-'
|
|
|
+ const d = new Date(time)
|
|
|
+ const pad = n => n < 10 ? '0' + n : n
|
|
|
+ return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) +
|
|
|
+ ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds())
|
|
|
+ },
|
|
|
+ formatPriceWithSymbol(amount) {
|
|
|
+ if (amount == null) return '-'
|
|
|
+ return '¥' + (amount / 100).toFixed(2)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.summary-bar {
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+</style>
|