211 lines
11 KiB
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>
|