javacodeadmin/ruoyi-ui/src/views/system/GoodsOrder/UnifiedRefundDialog.vue

422 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<el-dialog title="统一退款" :visible.sync="visible" width="900px" @close="handleClose">
<el-form ref="refundForm" :model="refundForm" label-width="100px">
<div class="refund-container">
<!-- 支付信息展示 -->
<div class="payment-info" v-if="paymentData">
<h4>支付信息</h4>
<el-row :gutter="20">
<el-col :span="8">
<div class="info-item">
<label>微信支付:</label>
<span class="amount">¥{{ paymentData.wechatAmount || '0.00' }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<label>余额支付:</label>
<span class="amount">¥{{ paymentData.balanceAmount || '0.00' }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<label>购物金:</label>
<span class="amount">¥{{ paymentData.shoppingGoldAmount || '0.00' }}</span>
</div>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<div class="info-item">
<label>服务金:</label>
<span class="amount">¥{{ paymentData.serviceGoldAmount || '0.00' }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<label>会员优惠:</label>
<span class="amount">¥{{ paymentData.memberDiscountAmount || '0.00' }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<label>优惠券抵扣:</label>
<span class="amount">¥{{ paymentData.couponAmount || '0.00' }}</span>
</div>
</el-col>
</el-row>
<div class="total-amount">
<label>总支付金额:</label>
<span class="amount total">¥{{ totalPaymentAmount }}</span>
</div>
<!-- 退款历史信息 -->
<div class="refund-history" v-if="refundHistory && refundHistory.length > 0">
<h5>退款历史</h5>
<div class="history-item" v-for="(item, index) in refundHistory" :key="index">
<span class="history-time">{{ formatTime(item.refundTime) }}</span>
<span class="history-amount">¥{{ item.totalRefund }}</span>
<span class="history-desc">{{ item.name }}</span>
</div>
<div class="history-summary">
<span>累计已退款:¥{{ totalRefundedAmount }}</span>
<span class="remaining">剩余可退款:¥{{ remainingRefundableAmount }}</span>
</div>
</div>
</div>
<!-- 退款金额输入 -->
<div class="refund-input">
<h4>本次退款金额</h4>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="微信退款:">
<el-input
v-model="refundForm.wechatRefund"
type="number"
placeholder="0.00"
min="0"
step="0.01"
@input="calculateTotalRefund"
>
<template slot="prepend">¥</template>
</el-input>
<div class="input-tip">最大可退:¥{{ paymentData.wechatAmount || '0.00' }}</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="余额退款:">
<el-input
v-model="refundForm.balanceRefund"
type="number"
placeholder="0.00"
min="0"
step="0.01"
@input="calculateTotalRefund"
>
<template slot="prepend">¥</template>
</el-input>
<div class="input-tip">最大可退:¥{{ paymentData.balanceAmount || '0.00' }}</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="购物金退款:">
<el-input
v-model="refundForm.shoppingGoldRefund"
type="number"
placeholder="0.00"
min="0"
step="0.01"
@input="calculateTotalRefund"
>
<template slot="prepend">¥</template>
</el-input>
<div class="input-tip">最大可退:¥{{ paymentData.shoppingGoldAmount || '0.00' }}</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="服务金退款:">
<el-input
v-model="refundForm.serviceGoldRefund"
type="number"
placeholder="0.00"
min="0"
step="0.01"
@input="calculateTotalRefund"
>
<template slot="prepend">¥</template>
</el-input>
<div class="input-tip">最大可退:¥{{ paymentData.serviceGoldAmount || '0.00' }}</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="会员优惠退款:">
<el-input
v-model="refundForm.memberDiscountRefund"
type="number"
placeholder="0.00"
min="0"
step="0.01"
@input="calculateTotalRefund"
>
<template slot="prepend">¥</template>
</el-input>
<div class="input-tip">最大可退:¥{{ paymentData.memberDiscountAmount || '0.00' }}</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="优惠券退款:">
<el-input
v-model="refundForm.couponRefund"
type="number"
placeholder="0.00"
min="0"
step="0.01"
@input="calculateTotalRefund"
>
<template slot="prepend">¥</template>
</el-input>
<div class="input-tip">最大可退:¥{{ paymentData.couponAmount || '0.00' }}</div>
</el-form-item>
</el-col>
</el-row>
<!-- 总退款金额显示 -->
<div class="total-refund">
<label>本次退款金额:</label>
<span class="amount refund">¥{{ totalRefundAmount }}</span>
</div>
<!-- 退款验证提示 -->
<div class="refund-validation" v-if="totalRefundAmount > 0">
<el-alert
:title="validationMessage"
:type="validationType"
show-icon
:closable="false"
/>
</div>
</div>
<!-- 退款备注 -->
<div class="refund-remark">
<el-form-item label="退款备注:">
<el-input
v-model="refundForm.refundRemark"
type="textarea"
:rows="3"
placeholder="请输入退款备注信息"
maxlength="200"
show-word-limit
/>
</el-form-item>
</div>
</div>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleConfirm" :loading="loading" :disabled="!canRefund">确认退款</el-button>
</div>
</el-dialog>
</template>
<script>
import { unifiedRefund } from "@/api/system/UsersPayBefor"
export default {
name: "UnifiedRefundDialog",
props: {
visible: { type: Boolean, default: false },
orderId: { type: String, default: "" },
paymentData: { type: Object, default: () => ({}) }
},
data() {
return {
loading: false,
refundForm: {
wechatRefund: "",
balanceRefund: "",
shoppingGoldRefund: "",
serviceGoldRefund: "",
memberDiscountRefund: "",
couponRefund: "",
refundRemark: ""
},
totalRefundAmount: "0.00",
refundHistory: [], // 退款历史
totalRefundedAmount: "0.00", // 累计已退款
remainingRefundableAmount: "0.00" // 剩余可退款
}
},
computed: {
totalPaymentAmount() {
if (!this.paymentData) return "0.00";
const total = (this.paymentData.wechatAmount || 0) + (this.paymentData.balanceAmount || 0) +
(this.paymentData.shoppingGoldAmount || 0) + (this.paymentData.serviceGoldAmount || 0) +
(this.paymentData.memberDiscountAmount || 0) + (this.paymentData.couponAmount || 0);
return total.toFixed(2);
},
canRefund() {
return parseFloat(this.totalRefundAmount) > 0 && parseFloat(this.totalRefundAmount) <= parseFloat(this.remainingRefundableAmount);
},
validationMessage() {
const totalRefund = parseFloat(this.totalRefundAmount);
const remaining = parseFloat(this.remainingRefundableAmount);
if (totalRefund > remaining) {
return `退款金额超过剩余可退款金额,最多可退:¥${this.remainingRefundableAmount}`;
} else if (totalRefund > 0) {
return `退款后将剩余可退款:¥${(remaining - totalRefund).toFixed(2)}`;
}
return "";
},
validationType() {
const totalRefund = parseFloat(this.totalRefundAmount);
const remaining = parseFloat(this.remainingRefundableAmount);
return totalRefund > remaining ? "error" : "success";
}
},
watch: {
visible(newVal) {
if (newVal) {
this.initForm();
this.loadRefundHistory();
}
}
},
methods: {
initForm() {
// 初始化表单,设置最大退款金额
this.refundForm.wechatRefund = "";
this.refundForm.balanceRefund = "";
this.refundForm.shoppingGoldRefund = "";
this.refundForm.serviceGoldRefund = "";
this.refundForm.memberDiscountRefund = "";
this.refundForm.couponRefund = "";
this.refundForm.refundRemark = "";
this.calculateTotalRefund();
},
async loadRefundHistory() {
try {
// 这里可以调用API获取退款历史
// const response = await getRefundHistory(this.orderId);
// this.refundHistory = response.data || [];
// 模拟数据实际应该从API获取
this.refundHistory = [];
this.totalRefundedAmount = "0.00";
this.remainingRefundableAmount = this.totalPaymentAmount;
} catch (error) {
console.error('获取退款历史失败:', error);
}
},
calculateTotalRefund() {
const total = (parseFloat(this.refundForm.wechatRefund) || 0) + (parseFloat(this.refundForm.balanceRefund) || 0) +
(parseFloat(this.refundForm.shoppingGoldRefund) || 0) + (parseFloat(this.refundForm.serviceGoldRefund) || 0) +
(parseFloat(this.refundForm.memberDiscountRefund) || 0) + (parseFloat(this.refundForm.couponRefund) || 0);
this.totalRefundAmount = total.toFixed(2);
},
validateRefundAmount() {
const totalRefund = parseFloat(this.totalRefundAmount);
const remaining = parseFloat(this.remainingRefundableAmount);
if (totalRefund <= 0) {
this.$message.error("退款金额必须大于0");
return false;
}
if (totalRefund > remaining) {
this.$message.error(`退款金额不能超过剩余可退款金额,最多可退:¥${this.remainingRefundableAmount}`);
return false;
}
return true;
},
async handleConfirm() {
if (!this.validateRefundAmount()) return;
this.loading = true;
try {
const params = {
orderId: this.orderId,
wechatRefund: this.refundForm.wechatRefund || "0",
balanceRefund: this.refundForm.balanceRefund || "0",
shoppingGoldRefund: this.refundForm.shoppingGoldRefund || "0",
serviceGoldRefund: this.refundForm.serviceGoldRefund || "0",
memberDiscountRefund: this.refundForm.memberDiscountRefund || "0",
couponRefund: this.refundForm.couponRefund || "0",
refundRemark: this.refundForm.refundRemark
};
const response = await unifiedRefund(params);
if (response.code === 200) {
this.$message.success("退款成功");
this.$emit("success", response.data);
this.handleClose();
} else {
this.$message.error(response.msg || "退款失败");
}
} catch (error) {
console.error("退款失败:", error);
this.$message.error("退款失败,请重试");
} finally {
this.loading = false;
}
},
handleClose() {
this.$emit("update:visible", false);
this.resetForm();
},
resetForm() {
this.refundForm = {
wechatRefund: "",
balanceRefund: "",
shoppingGoldRefund: "",
serviceGoldRefund: "",
memberDiscountRefund: "",
couponRefund: "",
refundRemark: ""
};
this.totalRefundAmount = "0.00";
this.loading = false;
},
formatTime(timeStr) {
if (!timeStr) return '';
const date = new Date(timeStr);
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;
}
}
}
</script>
<style scoped>
.refund-container { padding: 20px 0; }
.payment-info { background-color: #f8f9fa; padding: 20px; border-radius: 8px; margin-bottom: 20px; border: 1px solid #e9ecef; }
.payment-info h4 { margin: 0 0 15px 0; color: #303133; font-size: 16px; font-weight: 600; }
.info-item { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; padding: 8px 0; }
.info-item label { font-weight: 500; color: #606266; min-width: 80px; }
.amount { font-weight: 600; color: #E6A23C; }
.amount.total { font-size: 18px; color: #409EFF; }
.total-amount { text-align: center; margin-top: 15px; padding-top: 15px; border-top: 1px solid #e9ecef; }
.total-amount label { font-weight: 600; color: #303133; margin-right: 10px; }
/* 退款历史样式 */
.refund-history { margin-top: 20px; padding-top: 20px; border-top: 1px solid #e9ecef; }
.refund-history h5 { margin: 0 0 15px 0; color: #303133; font-size: 14px; font-weight: 600; }
.history-item { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; padding: 5px 0; font-size: 12px; }
.history-time { color: #909399; min-width: 120px; }
.history-amount { color: #F56C6C; font-weight: 600; min-width: 80px; text-align: right; }
.history-desc { color: #606266; flex: 1; margin-left: 10px; }
.history-summary { display: flex; justify-content: space-between; margin-top: 10px; padding-top: 10px; border-top: 1px solid #e9ecef; font-weight: 600; }
.remaining { color: #409EFF; }
.refund-input { background-color: #fff; padding: 20px; border-radius: 8px; border: 1px solid #e9ecef; margin-bottom: 20px; }
.refund-input h4 { margin: 0 0 15px 0; color: #303133; font-size: 16px; font-weight: 600; }
.total-refund { text-align: center; margin-top: 20px; padding-top: 20px; border-top: 1px solid #e9ecef; }
.total-refund label { font-weight: 600; color: #303133; margin-right: 10px; }
.amount.refund { font-size: 20px; color: #F56C6C; font-weight: bold; }
.input-tip { font-size: 11px; color: #909399; margin-top: 2px; }
.refund-validation { margin-top: 15px; }
.refund-remark { background-color: #fff; padding: 20px; border-radius: 8px; border: 1px solid #e9ecef; }
.dialog-footer { text-align: right; }
.el-form-item { margin-bottom: 15px; }
.el-input-group__prepend { background-color: #f5f7fa; color: #909399; font-weight: 500; }
</style>