242 lines
8.7 KiB
Vue
242 lines
8.7 KiB
Vue
<template>
|
||
<a-modal
|
||
title="订单退款"
|
||
:width="760"
|
||
:visible="visible"
|
||
:footer="null"
|
||
:maskClosable="false"
|
||
@cancel="handleCancel"
|
||
>
|
||
<a-spin :spinning="isLoading">
|
||
<div class="info">
|
||
<p>油卡编号:{{ record.oilCardNo }}</p>
|
||
<p>司机:{{ record.driver_name || '-' }} {{ record.mobile }}<a-tag v-if="record.source === 'mall'" class="ml-10">小程序订单</a-tag></p>
|
||
<p>订单金额:¥{{ record.total_price }} 已加油:¥{{ record.used_amount || '0.00' }}({{ record.used_num || 0 }} 次)</p>
|
||
<p>
|
||
退款金额:<b class="amount">¥{{ refundAmount }}</b>
|
||
<span v-if="status === 2">(实际退款)</span>
|
||
<span v-else>(剩余未加油金额,固定不可修改)</span>
|
||
</p>
|
||
<p>当前状态:<a-tag :color="statusColor">{{ statusText }}</a-tag></p>
|
||
<p v-if="record.refund_text" class="sub">{{ record.refund_text }}</p>
|
||
</div>
|
||
|
||
<a-alert v-if="(status === 0 || status === 3) && Number(refundAmount) <= 0" type="warning" show-icon class="block" message="订单金额已全部加油,没有可退金额" />
|
||
|
||
<!-- 可发起:未退款 / 退款失败,且还有剩余未加油金额 -->
|
||
<template v-if="(status === 0 || status === 3) && Number(refundAmount) > 0">
|
||
<a-alert type="info" show-icon class="block">
|
||
<div slot="message">
|
||
一张订单可以分多次加油,<b>退款金额固定为剩余未加油金额</b>,已加油的部分不退,每单只能成功退一次。
|
||
<br>退款会先用企油通 7.17 查询司机支付凭证,再用 7.16 把凭证额度调低退款金额;成功后{{ backText }}。
|
||
<br>额度在凭证上按司机累计,若凭证可用余额不足退款金额(被该司机其他订单用掉),会直接拒绝。
|
||
</div>
|
||
</a-alert>
|
||
<a-textarea v-model="reason" :rows="2" :maxLength="100" placeholder="退款原因(选填,会写入企油通调额记录)" />
|
||
<div class="btns">
|
||
<a-button type="primary" :loading="isLoading" @click="handleRefund">确认退款</a-button>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- 待人工确认 -->
|
||
<template v-if="status === 4">
|
||
<a-alert type="warning" show-icon class="block">
|
||
<div slot="message">
|
||
调额请求已发给企油通但没有拿到明确结果。企油通调额接口<b>没有幂等保护</b>,重复发起会被重复扣减,所以这里不允许直接重试。
|
||
<br>请先点「向企油通取证」:系统会查司机钱包流水(找退款单号)和凭证额度变化自动判定;判定不了再人工核对后提交。
|
||
</div>
|
||
</a-alert>
|
||
<div class="btns">
|
||
<a-button type="primary" :loading="isLoading" @click="handleConfirm">向企油通取证</a-button>
|
||
</div>
|
||
<a-divider orientation="left">人工判定</a-divider>
|
||
<a-radio-group v-model="resolveResult">
|
||
<a-radio value="success">企油通已调低额度(退款成功)</a-radio>
|
||
<a-radio value="failed">企油通未调额(退款失败,可重新发起)</a-radio>
|
||
</a-radio-group>
|
||
<a-textarea v-model="resolveRemark" class="mt-10" :rows="2" :maxLength="100" placeholder="判定依据(必填),如:已在企油通后台核对凭证 xxx 总额度由 300 变为 200" />
|
||
<div class="btns">
|
||
<a-button :loading="isLoading" @click="handleResolve">提交人工判定</a-button>
|
||
</div>
|
||
</template>
|
||
|
||
<a-divider orientation="left">退款流水</a-divider>
|
||
<a-table
|
||
size="small"
|
||
rowKey="refund_id"
|
||
:columns="columns"
|
||
:dataSource="logs"
|
||
:pagination="false"
|
||
>
|
||
<span slot="status" slot-scope="text, row">
|
||
<a-tag :color="logColor(row.status)">{{ row.status_text }}</a-tag>
|
||
</span>
|
||
</a-table>
|
||
</a-spin>
|
||
</a-modal>
|
||
</template>
|
||
|
||
<script>
|
||
import * as Api from '@/api/partners/order'
|
||
|
||
const STATUS_TEXT = { 0: '未退款', 1: '退款中', 2: '已退款', 3: '退款失败', 4: '待人工确认' }
|
||
const STATUS_COLOR = { 1: 'blue', 2: 'green', 3: 'red', 4: 'orange' }
|
||
|
||
export default {
|
||
data () {
|
||
return {
|
||
visible: false,
|
||
isLoading: false,
|
||
record: {},
|
||
reason: '',
|
||
resolveResult: 'success',
|
||
resolveRemark: '',
|
||
logs: [],
|
||
columns: [
|
||
{ title: '退款单号', dataIndex: 'refund_no' },
|
||
{ title: '状态', scopedSlots: { customRender: 'status' } },
|
||
{ title: '凭证', dataIndex: 'quota_id' },
|
||
{ title: '调额前可用', dataIndex: 'available_before' },
|
||
{ title: '说明', dataIndex: 'message' },
|
||
{ title: '操作人', dataIndex: 'operator' },
|
||
{ title: '时间', dataIndex: 'create_time_text' }
|
||
]
|
||
}
|
||
},
|
||
computed: {
|
||
status () {
|
||
return Number(this.record.refund_status || 0)
|
||
},
|
||
statusText () {
|
||
return STATUS_TEXT[this.status] || '-'
|
||
},
|
||
statusColor () {
|
||
return STATUS_COLOR[this.status] || ''
|
||
},
|
||
// 退款成功后钱退到哪里:合作商单回补合作商余额,小程序单退回下单用户余额
|
||
backText () {
|
||
return this.record.source === 'mall'
|
||
? '把钱退回下单用户的账户余额(按实付金额折算),并同步订单状态到用户端'
|
||
: '原路回补合作商余额,并推送油卡变动通知'
|
||
},
|
||
// 已退款显示实际退款金额;否则为剩余未加油金额(后端同口径计算)
|
||
refundAmount () {
|
||
if (this.status === 2) {
|
||
return Number(this.record.refund_amount || 0).toFixed(2)
|
||
}
|
||
const remain = Number(this.record.total_price || 0) - Number(this.record.used_amount || 0)
|
||
return Math.max(0, remain).toFixed(2)
|
||
}
|
||
},
|
||
methods: {
|
||
|
||
show (record) {
|
||
this.visible = true
|
||
this.record = { ...record }
|
||
this.reason = ''
|
||
this.resolveResult = 'success'
|
||
this.resolveRemark = ''
|
||
this.loadLogs()
|
||
},
|
||
|
||
handleCancel () {
|
||
this.visible = false
|
||
},
|
||
|
||
loadLogs () {
|
||
Api.refundLog({ orderId: this.record.order_id }).then(result => {
|
||
this.logs = result.data.list
|
||
// 流水状态码与订单退款状态一致:用最新一条同步弹窗状态,失败 / 转人工后界面跟着切换
|
||
if (this.logs.length) {
|
||
this.record = { ...this.record, refund_status: Number(this.logs[0].status), refund_text: this.logs[0].message }
|
||
}
|
||
})
|
||
},
|
||
|
||
logColor (status) {
|
||
return STATUS_COLOR[Number(status)] || ''
|
||
},
|
||
|
||
// 退款 / 取证 / 判定之后统一刷新:弹窗里的状态 + 父列表
|
||
afterAction (result, status) {
|
||
this.$message.success(result.message, 2)
|
||
if (status !== undefined) {
|
||
this.record = { ...this.record, refund_status: status, refund_text: result.message }
|
||
}
|
||
this.loadLogs()
|
||
this.$emit('handleSubmit')
|
||
},
|
||
|
||
// 业务失败时后端返回错误,request 拦截器已弹出提示;这里仍需刷新状态(可能已变成失败/待确认)
|
||
afterError () {
|
||
this.loadLogs()
|
||
this.$emit('handleSubmit')
|
||
},
|
||
|
||
handleRefund () {
|
||
this.$confirm({
|
||
title: '确认退款 ¥' + this.refundAmount + '?',
|
||
content: '退还剩余未加油金额:将调低该司机企油通凭证额度,成功后回补合作商余额。',
|
||
onOk: () => {
|
||
this.isLoading = true
|
||
return Api.refund({ orderId: this.record.order_id, reason: this.reason })
|
||
.then(result => this.afterAction(result, 2))
|
||
.catch(() => this.afterError())
|
||
.finally(() => {
|
||
this.isLoading = false
|
||
})
|
||
}
|
||
})
|
||
},
|
||
|
||
handleConfirm () {
|
||
this.isLoading = true
|
||
Api.refundConfirm({ orderId: this.record.order_id })
|
||
.then(result => this.afterAction(result, result.data.refundStatus))
|
||
.catch(() => this.afterError())
|
||
.finally(() => {
|
||
this.isLoading = false
|
||
})
|
||
},
|
||
|
||
handleResolve () {
|
||
if (!this.resolveRemark.trim()) {
|
||
this.$message.warning('请填写判定依据')
|
||
return
|
||
}
|
||
this.isLoading = true
|
||
Api.refundResolve({ orderId: this.record.order_id, result: this.resolveResult, remark: this.resolveRemark })
|
||
.then(result => this.afterAction(result, result.data.refundStatus))
|
||
.catch(() => this.afterError())
|
||
.finally(() => {
|
||
this.isLoading = false
|
||
})
|
||
}
|
||
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
.info p {
|
||
margin-bottom: 6px;
|
||
}
|
||
.amount {
|
||
color: #f5222d;
|
||
}
|
||
.sub {
|
||
color: #999;
|
||
font-size: 12px;
|
||
}
|
||
.block {
|
||
margin: 12px 0;
|
||
}
|
||
.btns {
|
||
margin-top: 12px;
|
||
text-align: right;
|
||
}
|
||
.mt-10 {
|
||
margin-top: 10px;
|
||
}
|
||
</style>
|