admin_new/src/modules/order/refund/RefundProcessListView.vue

211 lines
11 KiB
Vue

<script setup lang="ts">
/**
* F006 订单退款管理 - W15.3
*
* 沿用老 vue order/orderrefund.vue 字段:
* - 选择 + id + userId + supplierId + userEnterpriseId + enterpriseId
* - supplierOrderCode + payStatus + payMoney + refundTime + requestTime + remark + delFlag
*
* 端点: /order/refund/page 探测; 404 → FALLBACK seed (12 条)
*
* 状态: payStatus 1=待支付 2=已支付 / 退款处理状态: 审核中/已退款/已拒绝
*/
import { ref, reactive, onMounted, computed } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { httpGet, httpPost } from '@/utils/request'
import PageHeader from '@/components/PageHeader/PageHeader.vue'
import SearchBar from '@/components/SearchBar/SearchBar.vue'
import DataTable from '@/components/DataTable/DataTable.vue'
interface RefundRow {
id: string
userId: string
supplierId: string
userEnterpriseId: string
enterpriseId: string
supplierOrderCode: string
payStatus: number
payMoney: number
refundTime: string
requestTime: string
remark: string
delFlag: number
refundStatus: number // 扩展字段 (老 vue 没有, 用于退款流程: 1=审核中 2=已退款 3=已拒绝 4=已撤销)
}
const PAY_LABEL: Record<number, string> = { 1: '待支付', 2: '已支付' }
const REFUND_LABEL: Record<number, string> = { 1: '审核中', 2: '已退款', 3: '已拒绝', 4: '已撤销' }
const REFUND_TYPE: Record<number, string> = { 1: 'primary', 2: 'success', 3: 'danger', 4: 'info' }
const ENDPOINTS = ['/order/orderrefund/page', '/order/orderrefund/list']
const FALLBACK: RefundRow[] = [
{ id:'RF001', userId:'u10086', supplierId:'s20015', userEnterpriseId:'ue001', enterpriseId:'e008', supplierOrderCode:'SO20260701001', payStatus:2, payMoney:3280.00, refundTime:'2026-07-02 14:32:11', requestTime:'2026-07-01 11:25:00', remark:'商品质量问题', delFlag:0, refundStatus:2 },
{ id:'RF002', userId:'u10112', supplierId:'s20016', userEnterpriseId:'ue002', enterpriseId:'e009', supplierOrderCode:'SO20260701002', payStatus:2, payMoney:5680.00, refundTime:'', requestTime:'2026-07-03 09:42:18', remark:'发货延误', delFlag:0, refundStatus:1 },
{ id:'RF003', userId:'u10133', supplierId:'s20017', userEnterpriseId:'ue003', enterpriseId:'e010', supplierOrderCode:'SO20260702001', payStatus:2, payMoney:1880.00, refundTime:'2026-06-29 16:00:00', requestTime:'2026-06-28 13:18:30', remark:'规格不符', delFlag:0, refundStatus:3 },
{ id:'RF004', userId:'u10144', supplierId:'s20018', userEnterpriseId:'ue004', enterpriseId:'e011', supplierOrderCode:'SO20260702002', payStatus:1, payMoney:0, refundTime:'', requestTime:'', remark:'', delFlag:0, refundStatus:4 },
{ id:'RF005', userId:'u10155', supplierId:'s20019', userEnterpriseId:'ue005', enterpriseId:'e012', supplierOrderCode:'SO20260703001', payStatus:2, payMoney:4410.00, refundTime:'2026-07-04 11:08:55', requestTime:'2026-07-03 18:32:00', remark:'协商一致退款', delFlag:0, refundStatus:2 },
{ id:'RF006', userId:'u10166', supplierId:'s20020', userEnterpriseId:'ue006', enterpriseId:'e013', supplierOrderCode:'SO20260703002', payStatus:2, payMoney:13500.00, refundTime:'', requestTime:'2026-07-05 10:14:42', remark:'部分退款申请', delFlag:0, refundStatus:1 },
{ id:'RF007', userId:'u10177', supplierId:'s20021', userEnterpriseId:'ue007', enterpriseId:'e014', supplierOrderCode:'SO20260704001', payStatus:2, payMoney:3200.00, refundTime:'2026-07-06 14:30:00', requestTime:'2026-07-05 16:48:22', remark:'客户主动撤销', delFlag:0, refundStatus:4 },
{ id:'RF008', userId:'u10188', supplierId:'s20022', userEnterpriseId:'ue008', enterpriseId:'e015', supplierOrderCode:'SO20260704002', payStatus:2, payMoney:6920.00, refundTime:'', requestTime:'2026-07-06 09:55:08', remark:'物流丢件', delFlag:0, refundStatus:1 },
{ id:'RF009', userId:'u10199', supplierId:'s20023', userEnterpriseId:'ue009', enterpriseId:'e016', supplierOrderCode:'SO20260705001', payStatus:2, payMoney:9520.00, refundTime:'2026-07-02 17:20:14', requestTime:'2026-07-01 11:42:35', remark:'商品损坏', delFlag:0, refundStatus:2 },
{ id:'RF010', userId:'u10210', supplierId:'s20024', userEnterpriseId:'ue010', enterpriseId:'e017', supplierOrderCode:'SO20260705002', payStatus:2, payMoney:5360.00, refundTime:'2026-06-30 13:30:00', requestTime:'2026-06-28 16:18:50', remark:'客户拒收', delFlag:0, refundStatus:3 },
{ id:'RF011', userId:'u10221', supplierId:'s20025', userEnterpriseId:'ue011', enterpriseId:'e018', supplierOrderCode:'SO20260706001', payStatus:2, payMoney:18500.00, refundTime:'2026-07-05 14:18:50', requestTime:'2026-07-04 09:25:00', remark:'换货协商一致折现', delFlag:0, refundStatus:2 },
{ id:'RF012', userId:'u10232', supplierId:'s20026', userEnterpriseId:'ue012', enterpriseId:'e019', supplierOrderCode:'SO20260706002', payStatus:2, payMoney:2400.00, refundTime:'', requestTime:'2026-07-06 11:02:18', remark:'批次问题', delFlag:0, refundStatus:1 }
]
const STATUS_TABS = [
{ value: '', label: '全部' },
{ value: '1', label: '审核中' },
{ value: '2', label: '已退款' },
{ value: '3', label: '已拒绝' },
{ value: '4', label: '已撤销' }
]
type Col = {
prop?: string; label: string; width?: number; slot?: string; fixed?: 'right' | true; type?: 'status' | 'text' | 'selection'
}
const columns: Col[] = [
{ prop: 'id', label: '退款ID', width: 100, fixed: true },
{ prop: 'supplierOrderCode', label: '供应商订单号', width: 200, slot: 'orderCode' },
{ prop: 'userId', label: '用户ID', width: 90 },
{ prop: 'supplierId', label: '商户ID', width: 90 },
{ prop: 'enterpriseId', label: '商户企业ID', width: 110 },
{ prop: 'userEnterpriseId', label: '用户企业ID', width: 110 },
{ type: 'status', prop: 'payStatus', label: '原支付状态', width: 100 },
{ prop: 'payMoney', label: '实际支付金额', width: 120 },
{ type: 'status', prop: 'refundStatus', label: '退款状态', width: 100 },
{ prop: 'remark', label: '退款原因', width: 200, slot: 'remark' },
{ prop: 'requestTime', label: '申请时间', width: 160, slot: 'requestTime' },
{ prop: 'refundTime', label: '退款完成时间', width: 160, slot: 'refundTime' },
{ prop: 'actions', label: '操作', width: 200, fixed: 'right', slot: 'actions' }
]
const list = ref<RefundRow[]>([])
const total = ref(0)
const loading = ref(false)
const page = ref(1)
const limit = ref(10)
const filterKw = ref('')
const statusTab = ref('')
const dataSource = ref<'api' | 'mock' | 'loading'>('loading')
const apiError = ref<string>('')
async function load() {
loading.value = true
dataSource.value = 'loading'
for (const ep of ENDPOINTS) {
try {
const params: any = { page: page.value, limit: limit.value }
const data: any = await httpGet<any>(ep, params)
const rows: RefundRow[] = (data?.list || []).map((it: any) => ({ ...it, raw: it }))
list.value = rows
total.value = Number(data?.total || rows.length)
dataSource.value = 'api'
apiError.value = ''
loading.value = false
return
} catch {}
}
list.value = [...FALLBACK]
total.value = FALLBACK.length
dataSource.value = 'mock'
apiError.value = '后端 API 暂未实装, 已加载前端示例数据'
loading.value = false
}
const filtered = computed(() => {
const kw = filterKw.value.trim().toLowerCase()
let rows = list.value
if (statusTab.value) rows = rows.filter(r => String(r.refundStatus) === statusTab.value)
if (kw) rows = rows.filter(r =>
String(r.id).toLowerCase().includes(kw) ||
String(r.supplierOrderCode).toLowerCase().includes(kw) ||
String(r.userId).toLowerCase().includes(kw)
)
return rows
})
const searchFields: Array<{ key: string; label: string; type: 'text' }> = [
{ key: 'id', label: '退款ID', type: 'text' },
{ key: 'supplierOrderCode', label: '供应商订单号', type: 'text' },
{ key: 'userId', label: '用户ID', type: 'text' }
]
const filters = reactive<Record<string, any>>({})
function onSearch() {
filterKw.value = String(filters.id || filters.supplierOrderCode || filters.userId || '')
page.value = 1
}
function onStatusTab(v: string) { statusTab.value = v; page.value = 1 }
async function onApprove(row: RefundRow) {
try { await ElMessageBox.confirm(`确认通过退款申请「${row.id}」?`, '提示', { type: 'warning' }) } catch { return }
try { await httpPost('/order/orderrefund/updatePayStatus', { id: row.id, payStatus: 2 }); ElMessage.success('已通过'); load() }
catch (e: any) { ElMessage.error(e?.msg || '操作失败') }
}
async function onReject(row: RefundRow) {
try {
const { value } = await ElMessageBox.prompt('请填写拒绝原因', '拒绝退款', { inputPlaceholder: '原因', confirmButtonText: '拒绝', cancelButtonText: '取消' })
try { await httpPost('/order/orderrefund/updatePayStatus', { id: row.id, payStatus: 3, remark: value }); ElMessage.success('已拒绝'); load() }
catch (e: any) { ElMessage.error(e?.msg || '操作失败') }
} catch {}
}
function onView(row: RefundRow) {
ElMessage.info(`退款单号: ${row.id}`)
}
function onExport() {
ElMessage.success('已发起导出 (前端示例, 共 ' + filtered.value.length + ' 条)')
}
onMounted(load)
</script>
<template>
<div class="rr-page">
<PageHeader title="退款管理" sub="实物订单退款流程审核 + 跟踪" :data-source="dataSource" :api-error="apiError">
<el-button type="primary" @click="onExport">导出明细</el-button>
</PageHeader>
<el-tabs v-model="statusTab" @tab-change="onStatusTab" class="rr-tabs">
<el-tab-pane v-for="t in STATUS_TABS" :key="t.value" :name="t.value" :label="t.label" />
</el-tabs>
<SearchBar v-model:filters="filters" :fields="searchFields" @search="onSearch" />
<DataTable
:columns="columns"
:data="filtered"
:loading="loading"
:page="page"
:limit="limit"
:total="total"
row-key="id"
@page-change="(p: number) => { page = p; load() }"
@limit-change="(l: number) => { limit = l; page = 1; load() }"
>
<template #orderCode="{ row }">
<el-link type="primary" :underline="false" @click="onView(row)">{{ row.supplierOrderCode }}</el-link>
</template>
<template #remark="{ row }">
<span :class="{ 'rr-muted': !row.remark }">{{ row.remark || '--' }}</span>
</template>
<template #requestTime="{ row }">{{ row.requestTime || '--' }}</template>
<template #refundTime="{ row }">{{ row.refundTime || '--' }}</template>
<template #actions="{ row }">
<template v-if="row.refundStatus === 1">
<el-button type="success" link @click="onApprove(row)">通过</el-button>
<el-button type="danger" link @click="onReject(row)">拒绝</el-button>
</template>
<el-button v-else type="primary" link @click="onView(row)">详情</el-button>
</template>
</DataTable>
</div>
</template>
<style scoped>
.rr-page { padding: 16px; }
.rr-tabs { margin-bottom: 12px; }
.rr-muted { color: var(--ink-muted); }
</style>