This commit is contained in:
565
src/views/finance/refund/detail.vue
Normal file
565
src/views/finance/refund/detail.vue
Normal file
@@ -0,0 +1,565 @@
|
||||
<template>
|
||||
<div class="refund-detail-page">
|
||||
<ElCard shadow="never" v-loading="loading">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>退款详情</span>
|
||||
<ElButton @click="handleGoBack">返回</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<ElDescriptions :column="2" border v-if="refund">
|
||||
<ElDescriptionsItem label="退款单号">{{ refund.refund_no }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="状态">
|
||||
<ElTag :type="getStatusType(refund.status)">{{ getStatusText(refund.status) }}</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
|
||||
<ElDescriptionsItem label="订单ID">{{ refund.order_id }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="店铺ID">{{ refund.shop_id }}</ElDescriptionsItem>
|
||||
|
||||
<ElDescriptionsItem label="套餐使用记录ID">
|
||||
{{ refund.package_usage_id || '-' }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="申请退款金额">
|
||||
{{ formatCurrency(refund.requested_refund_amount) }}
|
||||
</ElDescriptionsItem>
|
||||
|
||||
<ElDescriptionsItem label="实际退款金额">
|
||||
{{
|
||||
refund.approved_refund_amount ? formatCurrency(refund.approved_refund_amount) : '-'
|
||||
}}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="实收金额">
|
||||
{{ formatCurrency(refund.actual_received_amount) }}
|
||||
</ElDescriptionsItem>
|
||||
|
||||
<ElDescriptionsItem label="资产重置">
|
||||
<ElTag :type="refund.asset_reset ? 'success' : 'info'">
|
||||
{{ refund.asset_reset ? '是' : '否' }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="佣金扣除">
|
||||
<ElTag :type="refund.commission_deducted ? 'success' : 'info'">
|
||||
{{ refund.commission_deducted ? '是' : '否' }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
|
||||
<ElDescriptionsItem label="退款原因" :span="2">
|
||||
{{ refund.refund_reason || '-' }}
|
||||
</ElDescriptionsItem>
|
||||
|
||||
<ElDescriptionsItem label="拒绝原因" :span="2">
|
||||
{{ refund.reject_reason || '-' }}
|
||||
</ElDescriptionsItem>
|
||||
|
||||
<ElDescriptionsItem label="审批备注" :span="2">
|
||||
{{ refund.remark || '-' }}
|
||||
</ElDescriptionsItem>
|
||||
|
||||
<ElDescriptionsItem label="创建人ID">{{ refund.creator }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="审批人ID">
|
||||
{{ refund.processor_id || '-' }}
|
||||
</ElDescriptionsItem>
|
||||
|
||||
<ElDescriptionsItem label="创建时间">
|
||||
{{ formatDateTime(refund.created_at) }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="审批时间">
|
||||
{{ refund.processed_at ? formatDateTime(refund.processed_at) : '-' }}
|
||||
</ElDescriptionsItem>
|
||||
|
||||
<ElDescriptionsItem label="更新时间">
|
||||
{{ formatDateTime(refund.updated_at) }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新人ID">{{ refund.updater }}</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
|
||||
<div class="action-buttons" v-if="refund">
|
||||
<!-- 待审批状态显示审批操作 -->
|
||||
<template v-if="refund.status === 1">
|
||||
<ElButton type="primary" @click="handleShowApprove">审批通过</ElButton>
|
||||
<ElButton type="danger" @click="handleShowReject">审批拒绝</ElButton>
|
||||
<ElButton type="warning" @click="handleShowReturn">退回</ElButton>
|
||||
</template>
|
||||
<!-- 已拒绝或已退回状态显示重新提交 -->
|
||||
<template v-if="refund.status === 3 || refund.status === 4">
|
||||
<ElButton type="primary" @click="handleShowResubmit">重新提交</ElButton>
|
||||
</template>
|
||||
</div>
|
||||
</ElCard>
|
||||
|
||||
<!-- 审批通过对话框 -->
|
||||
<ElDialog
|
||||
v-model="approveDialogVisible"
|
||||
title="审批通过"
|
||||
width="500px"
|
||||
@closed="handleApproveDialogClosed"
|
||||
>
|
||||
<ElForm
|
||||
ref="approveFormRef"
|
||||
:model="approveForm"
|
||||
:rules="approveRules"
|
||||
label-width="120px"
|
||||
>
|
||||
<ElFormItem label="退款单号">
|
||||
<span>{{ refund?.refund_no }}</span>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="申请退款金额">
|
||||
<span>{{ formatCurrency(refund?.requested_refund_amount || 0) }}</span>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="实际退款金额" prop="approved_refund_amount">
|
||||
<ElInputNumber
|
||||
v-model="approveForm.approved_refund_amount"
|
||||
:min="1"
|
||||
:precision="2"
|
||||
:step="1"
|
||||
style="width: 100%"
|
||||
placeholder="不填则默认使用申请金额"
|
||||
/>
|
||||
<div style="margin-top: 8px; font-size: 12px; color: var(--el-text-color-secondary)">
|
||||
不填则默认使用申请金额
|
||||
</div>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="审批备注">
|
||||
<ElInput
|
||||
v-model="approveForm.remark"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
placeholder="请输入审批备注"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="approveDialogVisible = false">取消</ElButton>
|
||||
<ElButton type="primary" @click="handleApproveRefund" :loading="approveLoading">
|
||||
确认通过
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
|
||||
<!-- 审批拒绝对话框 -->
|
||||
<ElDialog
|
||||
v-model="rejectDialogVisible"
|
||||
title="审批拒绝"
|
||||
width="500px"
|
||||
@closed="handleRejectDialogClosed"
|
||||
>
|
||||
<ElForm ref="rejectFormRef" :model="rejectForm" :rules="rejectRules" label-width="120px">
|
||||
<ElFormItem label="退款单号">
|
||||
<span>{{ refund?.refund_no }}</span>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="拒绝原因" prop="reject_reason">
|
||||
<ElInput
|
||||
v-model="rejectForm.reject_reason"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
placeholder="请输入拒绝原因"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="rejectDialogVisible = false">取消</ElButton>
|
||||
<ElButton type="danger" @click="handleRejectRefund" :loading="rejectLoading">
|
||||
确认拒绝
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
|
||||
<!-- 退回对话框 -->
|
||||
<ElDialog
|
||||
v-model="returnDialogVisible"
|
||||
title="退回退款申请"
|
||||
width="500px"
|
||||
@closed="handleReturnDialogClosed"
|
||||
>
|
||||
<ElForm ref="returnFormRef" :model="returnForm" :rules="returnRules" label-width="120px">
|
||||
<ElFormItem label="退款单号">
|
||||
<span>{{ refund?.refund_no }}</span>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="退回备注">
|
||||
<ElInput
|
||||
v-model="returnForm.remark"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
placeholder="请输入退回备注"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="returnDialogVisible = false">取消</ElButton>
|
||||
<ElButton type="warning" @click="handleReturnRefund" :loading="returnLoading">
|
||||
确认退回
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
|
||||
<!-- 重新提交对话框 -->
|
||||
<ElDialog
|
||||
v-model="resubmitDialogVisible"
|
||||
title="重新提交退款申请"
|
||||
width="500px"
|
||||
@closed="handleResubmitDialogClosed"
|
||||
>
|
||||
<ElForm
|
||||
ref="resubmitFormRef"
|
||||
:model="resubmitForm"
|
||||
:rules="resubmitRules"
|
||||
label-width="120px"
|
||||
>
|
||||
<ElFormItem label="退款单号">
|
||||
<span>{{ refund?.refund_no }}</span>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="申请退款金额">
|
||||
<ElInputNumber
|
||||
v-model="resubmitForm.requested_refund_amount"
|
||||
:min="1"
|
||||
:precision="2"
|
||||
:step="1"
|
||||
style="width: 100%"
|
||||
placeholder="不填则使用原金额"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="实收金额">
|
||||
<ElInputNumber
|
||||
v-model="resubmitForm.actual_received_amount"
|
||||
:min="1"
|
||||
:precision="2"
|
||||
:step="1"
|
||||
style="width: 100%"
|
||||
placeholder="不填则使用原金额"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="退款原因">
|
||||
<ElInput
|
||||
v-model="resubmitForm.refund_reason"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
maxlength="1000"
|
||||
show-word-limit
|
||||
placeholder="请输入退款原因"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="resubmitDialogVisible = false">取消</ElButton>
|
||||
<ElButton type="primary" @click="handleResubmitRefund" :loading="resubmitLoading">
|
||||
确认提交
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { RefundService } from '@/api/modules'
|
||||
import { ElMessage, ElTag } from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import type {
|
||||
Refund,
|
||||
RefundStatus,
|
||||
ApproveRefundRequest,
|
||||
RejectRefundRequest,
|
||||
ReturnRefundRequest,
|
||||
ResubmitRefundRequest
|
||||
} from '@/types/api'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
|
||||
defineOptions({ name: 'RefundDetail' })
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const loading = ref(false)
|
||||
const approveLoading = ref(false)
|
||||
const rejectLoading = ref(false)
|
||||
const returnLoading = ref(false)
|
||||
const resubmitLoading = ref(false)
|
||||
const refund = ref<Refund | null>(null)
|
||||
|
||||
const approveDialogVisible = ref(false)
|
||||
const rejectDialogVisible = ref(false)
|
||||
const returnDialogVisible = ref(false)
|
||||
const resubmitDialogVisible = ref(false)
|
||||
|
||||
const approveFormRef = ref<FormInstance>()
|
||||
const rejectFormRef = ref<FormInstance>()
|
||||
const returnFormRef = ref<FormInstance>()
|
||||
const resubmitFormRef = ref<FormInstance>()
|
||||
|
||||
const approveRules = reactive<FormRules>({})
|
||||
|
||||
const rejectRules = reactive<FormRules>({
|
||||
reject_reason: [{ required: true, message: '请输入拒绝原因', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
const returnRules = reactive<FormRules>({})
|
||||
|
||||
const resubmitRules = reactive<FormRules>({})
|
||||
|
||||
const approveForm = reactive<ApproveRefundRequest>({
|
||||
approved_refund_amount: undefined,
|
||||
remark: ''
|
||||
})
|
||||
|
||||
const rejectForm = reactive<RejectRefundRequest>({
|
||||
reject_reason: ''
|
||||
})
|
||||
|
||||
const returnForm = reactive<ReturnRefundRequest>({
|
||||
remark: ''
|
||||
})
|
||||
|
||||
const resubmitForm = reactive<{
|
||||
requested_refund_amount?: number
|
||||
actual_received_amount?: number
|
||||
refund_reason?: string
|
||||
}>({
|
||||
requested_refund_amount: undefined,
|
||||
actual_received_amount: undefined,
|
||||
refund_reason: ''
|
||||
})
|
||||
|
||||
// 格式化货币 - 将分转换为元
|
||||
const formatCurrency = (amount: number): string => {
|
||||
return `¥${(amount / 100).toFixed(2)}`
|
||||
}
|
||||
|
||||
// 获取状态标签类型
|
||||
const getStatusType = (
|
||||
status: RefundStatus
|
||||
): 'warning' | 'success' | 'danger' | 'info' => {
|
||||
const statusMap: Record<RefundStatus, 'warning' | 'success' | 'danger' | 'info'> = {
|
||||
1: 'warning', // 待审批
|
||||
2: 'success', // 已通过
|
||||
3: 'danger', // 已拒绝
|
||||
4: 'info' // 已退回
|
||||
}
|
||||
return statusMap[status] || 'info'
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status: RefundStatus): string => {
|
||||
const statusMap: Record<RefundStatus, string> = {
|
||||
1: '待审批',
|
||||
2: '已通过',
|
||||
3: '已拒绝',
|
||||
4: '已退回'
|
||||
}
|
||||
return statusMap[status] || '-'
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const id = route.params.id as string
|
||||
if (id) {
|
||||
fetchRefundDetail(Number(id))
|
||||
}
|
||||
})
|
||||
|
||||
// 获取退款详情
|
||||
const fetchRefundDetail = async (id: number) => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await RefundService.getRefundById(id)
|
||||
if (res.code === 0) {
|
||||
refund.value = res.data
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 返回
|
||||
const handleGoBack = () => {
|
||||
router.back()
|
||||
}
|
||||
|
||||
// 显示审批通过对话框
|
||||
const handleShowApprove = () => {
|
||||
approveDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 审批通过对话框关闭后的清理
|
||||
const handleApproveDialogClosed = () => {
|
||||
approveFormRef.value?.resetFields()
|
||||
approveForm.approved_refund_amount = undefined
|
||||
approveForm.remark = ''
|
||||
}
|
||||
|
||||
// 审批通过
|
||||
const handleApproveRefund = async () => {
|
||||
if (!approveFormRef.value || !refund.value) return
|
||||
|
||||
await approveFormRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
approveLoading.value = true
|
||||
try {
|
||||
const data: ApproveRefundRequest = {
|
||||
approved_refund_amount: approveForm.approved_refund_amount
|
||||
? approveForm.approved_refund_amount * 100
|
||||
: undefined,
|
||||
remark: approveForm.remark || undefined
|
||||
}
|
||||
|
||||
await RefundService.approveRefund(refund.value.id, data)
|
||||
ElMessage.success('审批通过成功')
|
||||
approveDialogVisible.value = false
|
||||
await fetchRefundDetail(refund.value.id)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
approveLoading.value = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 显示审批拒绝对话框
|
||||
const handleShowReject = () => {
|
||||
rejectDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 审批拒绝对话框关闭后的清理
|
||||
const handleRejectDialogClosed = () => {
|
||||
rejectFormRef.value?.resetFields()
|
||||
rejectForm.reject_reason = ''
|
||||
}
|
||||
|
||||
// 审批拒绝
|
||||
const handleRejectRefund = async () => {
|
||||
if (!rejectFormRef.value || !refund.value) return
|
||||
|
||||
await rejectFormRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
rejectLoading.value = true
|
||||
try {
|
||||
await RefundService.rejectRefund(refund.value.id, rejectForm)
|
||||
ElMessage.success('审批拒绝成功')
|
||||
rejectDialogVisible.value = false
|
||||
await fetchRefundDetail(refund.value.id)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
rejectLoading.value = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 显示退回对话框
|
||||
const handleShowReturn = () => {
|
||||
returnDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 退回对话框关闭后的清理
|
||||
const handleReturnDialogClosed = () => {
|
||||
returnFormRef.value?.resetFields()
|
||||
returnForm.remark = ''
|
||||
}
|
||||
|
||||
// 退回
|
||||
const handleReturnRefund = async () => {
|
||||
if (!returnFormRef.value || !refund.value) return
|
||||
|
||||
await returnFormRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
returnLoading.value = true
|
||||
try {
|
||||
await RefundService.returnRefund(refund.value.id, returnForm)
|
||||
ElMessage.success('退回成功')
|
||||
returnDialogVisible.value = false
|
||||
await fetchRefundDetail(refund.value.id)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
returnLoading.value = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 显示重新提交对话框
|
||||
const handleShowResubmit = () => {
|
||||
if (!refund.value) return
|
||||
// 预填充原有数据
|
||||
resubmitForm.requested_refund_amount = refund.value.requested_refund_amount / 100
|
||||
resubmitForm.actual_received_amount = refund.value.actual_received_amount / 100
|
||||
resubmitForm.refund_reason = refund.value.refund_reason
|
||||
resubmitDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 重新提交对话框关闭后的清理
|
||||
const handleResubmitDialogClosed = () => {
|
||||
resubmitFormRef.value?.resetFields()
|
||||
resubmitForm.requested_refund_amount = undefined
|
||||
resubmitForm.actual_received_amount = undefined
|
||||
resubmitForm.refund_reason = ''
|
||||
}
|
||||
|
||||
// 重新提交
|
||||
const handleResubmitRefund = async () => {
|
||||
if (!resubmitFormRef.value || !refund.value) return
|
||||
|
||||
await resubmitFormRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
resubmitLoading.value = true
|
||||
try {
|
||||
const data: ResubmitRefundRequest = {
|
||||
requested_refund_amount: resubmitForm.requested_refund_amount
|
||||
? resubmitForm.requested_refund_amount * 100
|
||||
: undefined,
|
||||
actual_received_amount: resubmitForm.actual_received_amount
|
||||
? resubmitForm.actual_received_amount * 100
|
||||
: undefined,
|
||||
refund_reason: resubmitForm.refund_reason || undefined
|
||||
}
|
||||
|
||||
await RefundService.resubmitRefund(refund.value.id, data)
|
||||
ElMessage.success('重新提交成功')
|
||||
resubmitDialogVisible.value = false
|
||||
await fetchRefundDetail(refund.value.id)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
resubmitLoading.value = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.refund-detail-page {
|
||||
padding: 20px;
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1038
src/views/finance/refund/index.vue
Normal file
1038
src/views/finance/refund/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user