Files
lilishop-ui/buyer/src/pages/home/orderCenter/AfterSale.vue

512 lines
13 KiB
Vue

<template>
<div class="wrapper">
<UserCenterLayout
:tabs="changeWay.map((item) => item.name)"
:active-tab="activeTab"
@tab-change="change"
>
<template #extra>
<div class="order-search">
<el-input
v-model="params.keywords"
size="large"
@keyup.enter="getList"
placeholder="输入订单号搜索"
/>
<el-button type="primary" @click="getList">搜索</el-button>
</div>
</template>
<!-- 列表 -->
<empty v-if="orderList.length === 0"/>
<div class="order-content" v-else>
<div class="order-cols">
<span>订单详情</span>
<span>金额</span>
<span>操作</span>
</div>
<div
class="order-list"
v-for="(order, orderIndex) in orderList"
:key="order.sn || orderIndex"
>
<div class="order-header">
<div class="order-sn">
{{ order.createTime }}<span>售后单号 {{ order.sn }}</span><span class="order-store" @click="shopPage(order.storeId)">{{ order.storeName }}</span>
</div>
<div class="order-status">
<el-tag size="small" :type="statusTagType(order.serviceStatus)">{{ filterOrderStatus(order.serviceStatus) }}</el-tag>
</div>
</div>
<div class="order-body">
<div class="col-detail">
<div class="goods-item">
<div class="goods-thumb">
<img
@click="goodsDetail(order.skuId, order.goodsId)"
class="hover-color"
:src="order.goodsImage"
loading="lazy"
decoding="async"
alt=""
@error="$event.target.style.visibility = 'hidden'"
/>
</div>
<div class="goods-info">
<div class="goods-name hover-color" @click="goodsDetail(order.skuId, order.goodsId)">{{ order.goodsName }}</div>
<div class="goods-num">共{{ order.num }}件</div>
</div>
</div>
</div>
<div class="col-amount">
<div class="order-amount">{{ $filters.unitPrice(order.applyRefundPrice, "¥") }}</div>
</div>
<div class="col-ops">
<el-button
v-if="order.serviceStatus == 'PASS' && order.serviceType != 'RETURN_MONEY'"
type="primary"
size="small"
@click="openModal(order)"
>提交物流</el-button>
<el-button link type="primary" @click="goDetail(order.sn)">售后详情</el-button>
<el-button
v-if="order.afterSaleAllowOperationVO && order.afterSaleAllowOperationVO.cancel"
link
type="danger"
@click="cancel(order.sn)"
>取消售后</el-button>
</div>
</div>
</div>
<el-skeleton size="large" fix v-if="spinShow"></el-skeleton>
</div>
<!-- 分页 -->
<div class="page-size">
<el-pagination v-model:current-page="params.pageNumber" v-model:page-size="params.pageSize"
:total="total" :page-sizes="[20, 50, 100]" @current-change="changePageNum"
@size-change="changePageSize" layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</div>
</UserCenterLayout>
<el-dialog v-model="logisticsShow" width="530">
<template #header><p>
<span>提交物流信息</span>
</p></template>
<div>
<div class="goods-list modal-goods">
<img @click="goodsDetail(singleOrder.skuId, singleOrder.goodsId)" class="hover-color"
:src="singleOrder.goodsImage" loading="lazy" decoding="async" alt=""/>
<div>
<div class="hover-color" @click="goodsDetail(singleOrder.skuId, singleOrder.goodsId)">
{{ singleOrder.goodsName }}
</div>
<div class="mt_10">
<span class="global_color"
>{{ $filters.unitPrice(singleOrder.flowPrice, "¥") }} </span> x {{ singleOrder.num }}
</div>
</div>
</div>
<el-form ref="formRef" :model="form" label-position="left" label-width="100px" :rules="rules">
<el-form-item label="物流公司" prop="logisticsId">
<el-select v-model="form.logisticsId" placeholder="请选择物流公司">
<el-option v-for="item in companyList" :value="item.id" :key="item.id">{{ item.name }}</el-option>
</el-select>
</el-form-item>
<el-form-item label="物流单号" prop="logisticsNo">
<el-input v-model="form.logisticsNo" placeholder="请填写物流单号"></el-input>
</el-form-item>
<el-form-item label="发货时间" prop="mDeliverTime">
<el-date-picker
type="date"
style="width:100%"
v-model="form.mDeliverTime"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="选择发货时间"
/>
</el-form-item>
</el-form>
</div>
<template #footer><div style="text-align: right">
<el-button @click="logisticsShow = false">关闭</el-button>
<el-button type="primary" :loading="submitLoading" @click="submitDelivery">提交</el-button>
</div></template>
</el-dialog>
</div>
</template>
<script setup>
import { nextTick, onMounted, reactive, ref } from "vue";
import { useRouter } from "vue-router";
import { Message, Modal } from "@/utils/message";
import {afterSaleList, cancelAfterSale} from '@/api/member.js';
import {afterSaleDelivery, getLogisticsCompany} from '@/api/order.js';
import {afterSaleStatusList} from '../enumeration.js'
defineOptions({ name: 'AfterSale' });
const router = useRouter();
const formRef = ref(null);
const orderList = ref([]); // 订单列表
const params = reactive({ // 请求参数
pageNumber: 1,
pageSize: 20,
keywords: '',
sn: '',
sort: 'createTime',
order: 'desc'
});
const total = ref(0); // 订单总数
const spinShow = ref(false); // 加载状态
const companyList = ref([]); // 物流公司列表
const logisticsShow = ref(false); // 物流信息modal
const singleOrder = ref({}); // 单独的售后信息
const form = reactive({ // 物流信息数据
afterSaleSn: '',
logisticsId: '',
logisticsNo: '',
mDeliverTime: ''
});
const rules = { // 必填校验
logisticsId: [{required: true, message: '请选择物流公司'}],
logisticsNo: [{required: true, message: '请填写物流编号'}],
mDeliverTime: [{required: true, message: '请选择发货时间'}]
};
const submitLoading = ref(false); // 提交加载状态
const changeWay = [
{ name: '全部', status: '' },
{ name: '申请中', status: 'APPLY' },
{ name: '通过', status: 'PASS' },
{ name: '完成售后', status: 'COMPLETE' },
];
const activeTab = ref(0);
function goDetail(sn) { // 跳转售后详情
router.push({
name: 'AfterSaleDetail',
query: {sn}
})
}
function cancel(sn) { // 取消售后申请
Modal.confirm({
title: '取消',
content: '确定取消此次售后申请吗?',
onOk: () => {
cancelAfterSale(sn).then(res => {
if (res.success) {
Message.success('取消售后申请成功')
getList()
}
})
},
onCancel: () => {
}
});
}
function goodsDetail(skuId, goodsId) {
// 跳转商品详情
let routeUrl = router.resolve({
path: '/goodsDetail',
query: {skuId, goodsId}
});
window.open(routeUrl.href, '_blank');
}
// 跳转店铺首页
function shopPage(id) {
router.push({
path: '/merchant',
query: {id: id}
});
}
function change(index) {
activeTab.value = index
params.pageNumber = 1
const current = changeWay[index]
if (!current || !current.status) {
delete params.serviceStatus
} else {
params.serviceStatus = current.status
}
getList()
}
function getList() { // 获取售后列表
spinShow.value = true;
let paramsData = JSON.parse(JSON.stringify(params))
afterSaleList(paramsData).then(res => {
spinShow.value = false
if (res.success) {
orderList.value = res.result.records;
total.value = res.result.total;
}
});
}
function changePageNum(val) { // 修改页码
params.pageNumber = val;
getList()
}
function changePageSize(val) { // 修改页数
params.pageNumber = 1;
params.pageSize = val;
getList()
}
function filterOrderStatus(status) { // 获取订单状态中文
const ob = afterSaleStatusList.filter(e => {
return e.status === status
});
return ob && ob[0] ? ob[0].name : status
}
function statusTagType(status) { // 售后状态对应标签类型
const map = {
APPLY: 'warning', APPLYING: 'warning',
COMPLETE: 'success',
CANCEL: 'info', EXPIRED: 'info'
}
return map[status] || 'info'
}
// 获取物流公司列表
function getCompany() {
getLogisticsCompany().then(res => {
if (res.success) {
companyList.value = res.result
}
})
}
// 提交物流信息
function submitDelivery() {
formRef.value?.validate((valid) => {
if (!valid) return;
submitLoading.value = true;
afterSaleDelivery(form).then(res => {
if (res.success) {
logisticsShow.value = false;
Message.success('提交成功');
getList();
}
submitLoading.value = false;
}).catch(() => {
submitLoading.value = false;
});
});
}
// 提交物流modal
function openModal(row) {
singleOrder.value = row;
logisticsShow.value = true;
if (!companyList.value.length) {
getCompany();
}
nextTick(() => {
formRef.value?.resetFields();
form.afterSaleSn = row.sn;
});
}
onMounted(() => {
getList();
});
</script>
<style scoped lang="scss">
.wrapper {
margin-bottom: 40px;
}
.order-search {
display: flex;
align-items: center;
width: 330px;
:deep(.el-input) {
flex: 1;
}
:deep(.el-button) {
margin-left: 8px;
}
}
.order-content {
--order-cols: minmax(0, 1fr) 120px 120px;
--order-gap: 12px;
padding-top: 14px;
}
.page-size {
margin: 15px 0;
display: flex;
justify-content: flex-end;
:deep(.el-pagination) {
justify-content: flex-end;
}
}
.order-cols {
display: grid;
grid-template-columns: var(--order-cols);
column-gap: var(--order-gap);
align-items: center;
padding: 0 16px 10px;
color: #999;
font-size: 13px;
span:first-child {
padding-left: 72px;
}
span:nth-child(n + 2) {
text-align: center;
}
}
.order-list {
border: 1px solid #eee;
border-radius: 8px;
margin-bottom: 12px;
background: #fff;
overflow: hidden;
.order-header {
display: grid;
grid-template-columns: var(--order-cols);
column-gap: var(--order-gap);
align-items: center;
padding: 10px 16px;
background: #f7f7f7;
border-bottom: 1px solid #f0f0f0;
}
.order-sn {
grid-column: 1 / 3;
display: flex;
align-items: center;
min-width: 0;
font-size: 14px;
color: #333;
line-height: 1.5;
span {
margin-left: 16px;
}
}
.order-store {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #438cde;
cursor: pointer;
&:hover {
color: $theme_color;
}
}
.order-status {
grid-column: 3;
min-width: 0;
text-align: center;
font-size: 14px;
color: #666;
line-height: 1.4;
}
.order-body {
display: grid;
grid-template-columns: var(--order-cols);
column-gap: var(--order-gap);
align-items: center;
padding: 14px 16px;
}
.goods-item {
display: grid;
grid-template-columns: 60px minmax(0, 1fr);
column-gap: 12px;
align-items: start;
width: 100%;
min-width: 0;
}
.goods-thumb {
width: 60px;
height: 60px;
border: 1px solid #f0f0f0;
overflow: hidden;
background: #f5f5f5;
img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
}
.goods-info {
min-width: 0;
overflow: hidden;
}
.goods-name {
max-width: 100%;
color: #333;
font-size: 16px;
line-height: 22px;
max-height: 44px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
word-break: break-all;
}
.goods-num {
margin-top: 6px;
color: #999;
font-size: 14px;
line-height: 1.4;
}
.col-amount,
.col-ops {
text-align: center;
}
.order-amount {
color: #333;
font-size: 16px;
font-weight: 600;
line-height: 1.4;
}
.col-ops {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
:deep(.el-button + .el-button) {
margin-left: 0;
}
}
}
.modal-goods {
display: flex;
padding: 5px;
margin-bottom: 10px;
img {
width: 60px;
height: 60px;
margin-right: 10px;
}
> div {
flex: 1;
}
}
</style>