mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-11-13 12:28:54 +08:00
530 lines
14 KiB
Vue
530 lines
14 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>
|
|
<!-- 列表 -->
|
|
<el-skeleton v-if="spinShow" :rows="5" animated />
|
|
<el-empty v-else-if="queryFailed" description="加载失败,请重试"><el-button type="primary" @click="getList">重新加载</el-button></el-empty>
|
|
<empty v-else-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>
|
|
|
|
</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 { catchHandledRequestError } from "@/utils/requestError";
|
|
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 queryFailed = ref(false); // 区分加载失败与暂无记录,支持重新查询
|
|
let listRequestVersion = 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() { // 获取售后列表
|
|
const version = ++listRequestVersion;
|
|
queryFailed.value = false;
|
|
spinShow.value = true;
|
|
let paramsData = JSON.parse(JSON.stringify(params))
|
|
afterSaleList(paramsData).then(res => {
|
|
if (version !== listRequestVersion) return;
|
|
if (res.success) {
|
|
orderList.value = res.result.records;
|
|
total.value = res.result.total;
|
|
}
|
|
}).catch((error) => {
|
|
if (version !== listRequestVersion) {
|
|
catchHandledRequestError(error);
|
|
return;
|
|
}
|
|
queryFailed.value = true;
|
|
orderList.value = [];
|
|
total.value = 0;
|
|
catchHandledRequestError(error);
|
|
}).finally(() => {
|
|
if (version === listRequestVersion) spinShow.value = false;
|
|
});
|
|
}
|
|
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>
|