You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
shop-admin/src/views/sales/service/detail.vue

520 lines
23 KiB

<template>
<div v-loading="state.loading" class="detail-container">
<el-steps :active="state.currentStep" align-center>
<el-step
v-for="(item, index) in [[], state.steps, state.steps2][state.detail.refundType || 0]"
:key="index"
:description="item.desc"
:title="item.title"
/>
</el-steps>
<div class="card">
<div class="header">
<h3 class="left red">当前服务单状态:{{ state.detail.refundStatusDesc }}</h3>
<div class="right">
<!-- 待处理 -->
<template v-if="state.detail.refundStatus === 1">
<el-button type="primary" @click="handleResolveRefundOrReturn">
同意{{ state.detail.refundType === 1 ? '退款' : '退货' }}
</el-button>
<el-button type="danger" @click="handleRejectRefundOrReturn">
拒绝{{ state.detail.refundType === 1 ? '退款' : '退货' }}
</el-button>
</template>
<!-- 待收货 -->
<template v-if="state.detail.refundStatus === 4">
<el-button type="primary" @click="handleResolveReceive">确认收货</el-button>
<el-button type="danger" @click="handleRejectReceive">拒绝收货</el-button>
</template>
</div>
</div>
<el-scrollbar class="body">
<h3>售后商品</h3>
<el-table border :data="[state.detail.refundProduct]">
<el-table-column align="center" header-align="center" label="商品图片" prop="productImageUrl">
<template #default="{ row }">
<el-image :alt="row.productName || '商品图片'" height="64px" :src="row.productImageUrl" />
</template>
</el-table-column>
<el-table-column align="center" header-align="center" label="商品名称" prop="productName" />
<el-table-column align="center" header-align="center" label="价格" prop="realPrice">
<template #default="{ row }">¥{{ new Number(row.realPrice).toFixed(2) }}</template>
</el-table-column>
<el-table-column align="center" header-align="center" label="属性" prop="skuDescribe" />
<el-table-column align="center" header-align="center" label="数量" prop="quantity" />
<el-table-column align="center" header-align="center" label="小计" prop="realAmount">
<template #default="{ row }">¥{{ new Number(row.realAmount).toFixed(2) }}</template>
</el-table-column>
</el-table>
<h3 class="summary">
<span>合计:</span>
<span class="red">¥{{ new Number(state.sum).toFixed(2) }}</span>
</h3>
<br />
<h3>服务单信息</h3>
<el-form class="half-form" inline label-width="120px">
<el-form-item class="half" label="服务单号">
{{ state.detail.refundNo }}
</el-form-item>
<el-form-item class="half" label="处理状态">
{{ state.detail.refundStatusDesc }}
</el-form-item>
<el-form-item class="half" label="订单编号">
{{ state.detail.refundTradeOrderInfo?.orderNo }}
</el-form-item>
<el-form-item class="half" label="订单状态">
{{ state.detail.refundTradeOrderInfo?.orderStatusDesc }}
</el-form-item>
<el-form-item label="申请时间">
{{ state.detail.applyTime }}
</el-form-item>
<!-- <el-form-item label="用户账号">
{{ state.detail.userId }}
</el-form-item> -->
<el-form-item label="联系人">
{{ state.detail.userNickName }}
</el-form-item>
<el-form-item label="联系电话">
{{ state.detail.userPhone }}
</el-form-item>
<el-form-item label="退货原因">
{{ state.detail.refundReason }}
</el-form-item>
<el-form-item label="申请退款金额">
{{ state.detail.applyAmount }}
</el-form-item>
<el-form-item label="问题描述">
{{ state.detail.problemDescribe }}
</el-form-item>
<el-form-item label="凭证照片">
<el-image
v-for="(item, index) in state.detail.refundEvidences"
:key="index"
alt="凭证照片"
:src="item.fileUrl"
/>
</el-form-item>
</el-form>
<!-- 仅退款 -->
<template v-if="state.detail.refundType === 1">
<h3>退款处理</h3>
<!-- 待处理 -->
<el-form
v-if="state.detail.refundStatus === 1"
ref="refsForm"
label-width="120px"
:model="state.form"
:rules="state.rules"
>
<el-form-item label="订单金额">
{{ state.detail.refundAmount }}
</el-form-item>
<el-form-item label="确认退款金额" prop="refundAmount">
<el-input-number v-model="state.form.refundAmount" />
</el-form-item>
<el-form-item label="处理备注" prop="remark">
<el-input v-model="state.form.remark" />
<p class="tips">展示给用户的说明</p>
</el-form-item>
</el-form>
<!-- 已处理 -->
<el-form v-else label-width="120px">
<el-form-item label="订单金额">
{{ state.detail.productAmount }}
</el-form-item>
<el-form-item label="退运费">
{{ state.detail.isBackShippingAmount ? '退运费' : '不退运费' }}
</el-form-item>
<el-form-item label="确认退款金额" prop="refundAmount">
{{ state.detail.refundAmount }}
</el-form-item>
<el-form-item label="处理人员">
{{ state.detail.handleRefundLog?.createUserNickName }}
</el-form-item>
<el-form-item label="处理操作">
{{ state.detail.handleRefundLog?.operationTypeDesc }}
</el-form-item>
<el-form-item label="处理时间">
{{ state.detail.handleRefundLog?.createTime }}
</el-form-item>
<el-form-item label="处理备注">
{{ state.detail.remark || state.detail.closeReason || '无' }}
</el-form-item>
</el-form>
</template>
<!-- 退货退款 -->
<template v-else>
<h3>退货处理</h3>
<!-- 待处理 -->
<el-form
v-if="state.detail.refundStatus === 1"
ref="refsForm"
label-width="120px"
:model="state.form"
:rules="state.rules"
>
<el-form-item label="订单金额">
{{ state.detail.productAmount }}
</el-form-item>
<el-form-item label="退运费" prop="isBackShippingAmount">
<el-radio-group v-model="state.form.isBackShippingAmount" :opts="opts.shipFees" />
</el-form-item>
<el-form-item label="确认退款金额" prop="refundAmount">
<el-input-number v-model="state.form.refundAmount" />
</el-form-item>
<el-form-item label="选择收货点" prop="receivePoint">
<el-select
v-model="state.form.receivePoint"
:config="{ label: 'recipientName', value: 'detailAddress' }"
:opts="[state.detail.refundAddress]"
@change="handlePointChange"
/>
</el-form-item>
<el-form-item label="收货人姓名" prop="recipientName">
<el-input v-model="state.form.recipientName" />
</el-form-item>
<el-form-item label="所在区域" prop="address">
<el-area v-model="state.form.address" v-model:info="state.form.addressInfo" />
</el-form-item>
<el-form-item label="详细地址" prop="detailAddress">
<el-input v-model="state.form.detailAddress" />
</el-form-item>
<el-form-item label="联系电话" prop="recipientPhone">
<el-input v-model="state.form.recipientPhone" />
</el-form-item>
<el-form-item label="退货说明" prop="remark">
<el-input v-model="state.form.remark" />
<p class="tips">展示给用户的说明</p>
</el-form-item>
</el-form>
<el-form v-else label-width="120px">
<el-form-item label="订单金额">
{{ state.detail.productAmount }}
</el-form-item>
<el-form-item label="退运费">
{{ state.detail.isBackShippingAmount ? '退运费' : '不退运费' }}
</el-form-item>
<el-form-item label="确认退款金额">
{{ state.detail.refundAmount }}
</el-form-item>
<el-form-item label="收货人姓名">
{{ state.detail.refundAddress?.recipientName }}
</el-form-item>
<el-form-item label="收货地址">
{{ state.detail.refundAddress?.detailAddress }}
</el-form-item>
<el-form-item label="联系电话">
{{ state.detail.refundAddress?.recipientPhone }}
</el-form-item>
<el-form-item label="退货说明">
{{ state.detail.handleReturnLog?.remark }}
</el-form-item>
<el-form-item label="处理人员">
{{ state.detail.handleReturnLog?.createUserNickName }}
</el-form-item>
<el-form-item label="处理操作">
{{ state.detail.handleReturnLog?.operationTypeDesc }}
</el-form-item>
<el-form-item label="处理时间">
{{ state.detail.handleReturnLog?.createTime }}
</el-form-item>
</el-form>
<template v-if="state.detail.refundLogistics">
<h3>退货物流</h3>
<el-form label-width="120px">
<el-form-item label="物流信息">
<template v-if="state.detail.refundLogistics">
<span>
{{ state.detail.refundLogistics?.companyName }}
</span>
<span style="margin: 0 10px">
{{ state.detail.refundLogistics?.trackingNo }}
</span>
<el-button type="text" @click="handleTrack">查看</el-button>
</template>
<span v-else>暂无</span>
</el-form-item>
<el-form-item label="退款说明">
{{ state.detail.handleReturnLog?.problemDescribe }}
</el-form-item>
<el-form-item label="凭证照片">
<el-image
v-for="(item, index) in state.detail.logisticsEvidences"
:key="index"
alt="凭证照片"
:src="item.fileUrl"
/>
</el-form-item>
</el-form>
</template>
<template v-if="state.detail.refundStatus === 4">
<h3>收货处理</h3>
<el-form ref="refsForm" label-width="120px" :model="state.form" :rules="state.rules">
<el-form-item label="收货备注" prop="remark">
<el-input v-model="state.form.remark" />
</el-form-item>
</el-form>
</template>
</template>
</el-scrollbar>
</div>
<OrderTrack ref="refsOrderTrack" />
</div>
</template>
<script setup lang="jsx">
import OrderTrack from '../order/track.vue';
const store = useStore();
const route = useRoute();
const { proxy } = getCurrentInstance();
const opts = computed(() => store.state.service.opts);
if (!unref(opts).init) {
store.dispatch('service/load');
}
const refsForm = ref(null);
const state = reactive({
steps: [
{
title: '买家申请退款',
desc: '',
status: [1, 2],
},
{
title: '商家处理售后申请',
desc: '未处理',
status: [5, 6],
},
{
title: '售后完毕',
desc: '未完毕',
status: [3, 4, 5, 6],
},
],
steps2: [
{
title: '买家申请退货退款',
desc: '',
status: [1, 2],
},
{
title: '商家处理售后申请',
desc: '未处理',
status: [8, 9],
},
{
title: '买家寄回退货商品',
desc: '未寄回',
status: [11],
},
{
title: '商家确认收货',
desc: '未收货',
status: [13, 14],
},
{
title: '售后完毕',
desc: '未完毕',
status: [3, 4, 7, 10, 12, 13, 14],
},
],
currentStep: 1,
loading: false,
detail: {
refundProduct: [],
},
form: {
reject: false,
address: [],
addressInfo: [],
receivePoint: null,
isBackShippingAmount: 0,
area: '',
areaCode: '',
city: '',
cityCode: '',
detailAddress: '',
province: '',
provinceCode: '',
recipientName: '',
recipientPhone: '',
refundAmount: 0,
refundId: 0,
remark: '',
},
rules: {
refundAmount: [{ required: true, message: '退款金额不能为空' }],
recipientName: [{ required: true, message: '收件人姓名不能为空' }],
address: [{ required: true, message: '所在区域不能为空' }],
detailAddress: [{ required: true, message: '详细地址不能为空' }],
recipientPhone: [{ required: true, message: '联系电话不能为空' }],
remark: [
{
validator: (rule, value, cb) => {
if (state.form.reject && !value) {
let msg = '不能为空';
if (state.detail.refundType === 1) {
msg = '拒绝退款时处理备注' + msg;
} else {
if (state.detail.refundStatus === 4) {
msg = '拒绝收货时收货备注' + msg;
} else {
msg = '拒绝退货时处理备注' + msg;
}
}
cb(msg);
} else {
cb();
}
},
},
],
},
sum: computed(() => [state.detail.refundProduct].reduce((sum, current) => sum + current.realAmount || 0, 0)),
});
const handleLoad = async () => {
state.loading = true;
let res = await store.dispatch(
state.detail.refundType === 1 ? 'service/refundDetail' : 'service/returnDetail',
route.params.id
);
Object.assign(
state.detail,
res || {
refundProduct: [],
}
);
handlePointChange(state.detail.refundAddress.detailAddress);
state.form.refundId = res.refundId;
state.form.refundAmount = res.refundAmount;
[null, state.steps, state.steps2][res.refundType].forEach((step, index) => {
let date = res.refundLogs.find((item) => step.status.includes(item.operationType));
step.desc = date?.createTime || step.desc;
if (date) {
if (index >= 3) {
step.desc = date?.operationTypeDesc;
}
state.currentStep = index + 1;
}
});
state.loading = false;
};
onActivated(handleLoad);
const handlePointChange = (e) => {
state.form.receivePoint = e;
let point = [state.detail.refundAddress].find((item) => item.detailAddress === e);
state.form.recipientName = point?.recipientName || null;
state.form.recipientPhone = point?.recipientPhone || null;
state.form.detailAddress = point?.detailAddress || null;
state.form.address = point ? [point.provinceCode, point.cityCode, point.areaCode] : [];
state.form.addressInfo = point ? [point.province, point.city, point.area] : [];
};
const refsOrderTrack = ref(null);
const handleTrack = () => {
unref(refsOrderTrack).show(state.detail.orderId);
};
/* 同意退货/退款 */
const handleResolveRefundOrReturn = async () => {
state.loading = true;
state.form.reject = false;
try {
await proxy.$validate(refsForm);
await store.dispatch(
state.detail.refundType === 1 ? 'service/resolveRefund' : 'service/resolveReturn',
state.form
);
handleLoad();
} catch (e) {
console.info('取消同意', e);
}
state.loading = false;
};
/* 拒绝退货/退款 */
const handleRejectRefundOrReturn = async () => {
state.loading = true;
state.form.reject = true;
try {
await proxy.$validate(refsForm);
await store.dispatch(
state.detail.refundType === 1 ? 'service/rejectRefund' : 'service/rejectReturn',
state.form
);
handleLoad();
} catch (e) {
console.info('取消拒绝', e);
}
state.loading = false;
};
/* 确认收货 */
const handleResolveReceive = async () => {
state.loading = true;
state.form.reject = false;
try {
await proxy.$validate(refsForm);
await store.dispatch('service/resolveReceive', state.form);
handleLoad();
} catch (e) {
console.info('取消确认收货', e);
}
state.loading = false;
};
/* 拒绝收货 */
const handleRejectReceive = async () => {
state.loading = true;
state.form.reject = true;
try {
await proxy.$validate(refsForm);
await store.dispatch('service/rejectReceive', state.form);
handleLoad();
} catch (e) {
console.info('取消拒绝收货', e);
}
state.loading = false;
};
</script>
<style lang="less" scoped>
.detail-container {
display: flex;
flex-direction: column;
.card {
height: 100%;
flex-shrink: 1;
overflow: hidden;
margin-top: @layout-space;
display: flex;
flex-direction: column;
.header {
display: flex;
justify-content: space-between;
padding: @layout-space;
}
.body {
height: 100%;
flex-shrink: 1;
display: flex;
flex-direction: column;
padding: @layout-space;
h3 {
margin-bottom: @layout-space;
}
.half-form {
.el-form-item {
width: 100%;
&.half {
width: 50%;
margin-right: 0;
}
}
}
.summary {
text-align: right;
margin-top: @layout-space;
}
}
.red {
color: var(--el-color-danger);
}
}
}
</style>