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.
 
 
 
 

632 lines
14 KiB

<template>
<view class="after-sale-detail">
<!-- 状态头部 -->
<view class="status-header">
<view
class="status-badge"
:class="[getStatusClass(afterSaleDetail.status)]"
>
{{ getStatusText(afterSaleDetail.status) }}
</view>
<view class="status-desc">{{
getStatusDesc(afterSaleDetail.status)
}}</view>
</view>
<!-- 商品信息 -->
<view class="product-section">
<view class="section-title">商品信息</view>
<view class="product-info">
<image
class="product-image"
:src="afterSaleDetail.productImage"
mode="aspectFill"
/>
<view class="product-details">
<text class="product-title">{{ afterSaleDetail.productTitle }}</text>
<text class="product-spec">{{ afterSaleDetail.productSpec }}</text>
<text class="order-number"
>订单号{{ afterSaleDetail.orderNumber }}</text
>
</view>
</view>
</view>
<!-- 售后信息 -->
<view class="after-sale-section">
<view class="section-title">售后信息</view>
<view class="info-list">
<view class="info-item">
<text class="info-label">售后类型</text>
<text class="info-value">{{ afterSaleDetail.type }}</text>
</view>
<view class="info-item">
<text class="info-label">售后原因</text>
<text class="info-value">{{ afterSaleDetail.reason }}</text>
</view>
<view class="info-item">
<text class="info-label">申请时间</text>
<text class="info-value">{{
formatTime(afterSaleDetail.createTime)
}}</text>
</view>
<view class="info-item">
<text class="info-label">售后单号</text>
<text class="info-value">{{ afterSaleDetail.afterSaleNumber }}</text>
</view>
</view>
</view>
<!-- 问题描述 -->
<view class="description-section">
<view class="section-title">问题描述</view>
<view class="description-content">
<text class="description-text">{{ afterSaleDetail.description }}</text>
</view>
</view>
<!-- 问题图片 -->
<view
class="images-section"
v-if="afterSaleDetail.images && afterSaleDetail.images.length > 0"
>
<view class="section-title">问题图片</view>
<view class="images-grid">
<image
class="problem-image"
v-for="(image, index) in afterSaleDetail.images"
:key="index"
:src="image"
mode="aspectFill"
@click="previewImage(index)"
/>
</view>
</view>
<!-- 处理进度 -->
<view class="progress-section">
<view class="section-title">处理进度</view>
<view class="progress-list">
<view
class="progress-item"
v-for="(progress, index) in afterSaleDetail.progressList"
:key="index"
:class="{
active: progress.isActive,
completed: progress.isCompleted,
}"
>
<view class="progress-icon">
<uni-icons
:type="progress.isCompleted ? 'checkmarkempty' : 'circle'"
size="20"
:color="progress.isCompleted ? '#34c759' : '#ddd'"
/>
</view>
<view class="progress-content">
<text class="progress-title">{{ progress.title }}</text>
<text class="progress-time">{{ formatTime(progress.time) }}</text>
<text class="progress-desc" v-if="progress.description">{{
progress.description
}}</text>
</view>
</view>
</view>
</view>
<!-- 客服回复 -->
<view class="reply-section" v-if="afterSaleDetail.reply">
<view class="section-title">客服回复</view>
<view class="reply-content">
<text class="reply-text">{{ afterSaleDetail.reply.content }}</text>
<text class="reply-time">{{
formatTime(afterSaleDetail.reply.time)
}}</text>
</view>
</view>
<!-- 底部操作 -->
<view class="bottom-actions" v-if="showBottomActions">
<button
class="action-btn secondary"
@click="cancelAfterSale"
v-if="afterSaleDetail.status === 'pending'"
>
取消申请
</button>
<button class="action-btn primary" @click="contactService">
联系客服
</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
afterSaleId: "",
afterSaleDetail: {
id: "",
status: "pending",
productImage: "",
productTitle: "",
productSpec: "",
orderNumber: "",
type: "",
reason: "",
description: "",
images: [],
createTime: "",
afterSaleNumber: "",
progressList: [],
reply: null,
},
};
},
computed: {
showBottomActions() {
return ["pending", "processing"].includes(this.afterSaleDetail.status);
},
},
onLoad(options) {
if (options.id) {
this.afterSaleId = options.id;
this.loadAfterSaleDetail();
}
},
methods: {
// 加载售后详情
async loadAfterSaleDetail() {
try {
uni.showLoading({
title: "加载中...",
});
this.Post(
{ id: this.afterSaleId },
"/framework/afterSale/detail",
"DES"
)
.then((res) => {
uni.hideLoading();
if (res.code === 200) {
this.afterSaleDetail = res.data;
} else {
uni.showToast({
title: res.msg || "加载失败",
icon: "none",
});
}
})
.catch((error) => {
uni.hideLoading();
console.error("加载售后详情失败:", error);
uni.showToast({
title: "加载失败",
icon: "none",
});
});
} catch (error) {
uni.hideLoading();
console.error("加载售后详情失败:", error);
uni.showToast({
title: "加载失败",
icon: "none",
});
}
},
// 预览图片
previewImage(index) {
uni.previewImage({
current: index,
urls: this.afterSaleDetail.images,
});
},
// 取消售后申请
cancelAfterSale() {
uni.showModal({
title: "确认取消",
content: "确定要取消这个售后申请吗?",
success: (res) => {
if (res.confirm) {
this.Post(
{ id: this.afterSaleId },
"/framework/afterSale/cancel",
"DES"
)
.then((res) => {
if (res.code === 200) {
uni.showToast({
title: "取消成功",
icon: "success",
});
// 重新加载详情
this.loadAfterSaleDetail();
} else {
uni.showToast({
title: res.msg || "取消失败",
icon: "none",
});
}
})
.catch((error) => {
console.error("取消售后申请失败:", error);
uni.showToast({
title: "取消失败",
icon: "none",
});
});
}
},
});
},
// 联系客服
contactService() {
// 这里可以跳转到客服页面或拨打电话
uni.showModal({
title: "联系客服",
content: "请拨打客服电话:400-123-4567",
confirmText: "拨打",
success: (res) => {
if (res.confirm) {
// 拨打电话
uni.makePhoneCall({
phoneNumber: "400-123-4567",
});
}
},
});
},
// 获取状态文本
getStatusText(status) {
const statusMap = {
pending: "待处理",
processing: "处理中",
completed: "已完成",
cancelled: "已取消",
};
return statusMap[status] || "未知状态";
},
// 获取状态描述
getStatusDesc(status) {
const descMap = {
pending: "您的售后申请已提交,请耐心等待处理",
processing: "客服正在处理您的售后申请",
completed: "您的售后申请已处理完成",
cancelled: "您的售后申请已取消",
};
return descMap[status] || "";
},
// 获取状态样式类
getStatusClass(status) {
const classMap = {
pending: "status-pending",
processing: "status-processing",
completed: "status-completed",
cancelled: "status-cancelled",
};
return classMap[status] || "status-default";
},
// 格式化时间
formatTime(time) {
if (!time) return "";
const date = new Date(time);
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 lang="scss" scoped>
.after-sale-detail {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: calc(120rpx + env(safe-area-inset-bottom));
padding-bottom: calc(120rpx + constant(safe-area-inset-bottom));
}
// 状态头部
.status-header {
background-color: #fff;
padding: 40rpx 30rpx;
text-align: center;
margin-bottom: 20rpx;
}
.status-badge {
display: inline-block;
padding: 12rpx 24rpx;
border-radius: 20rpx;
font-size: 28rpx;
color: #fff;
font-weight: 600;
margin-bottom: 16rpx;
&.status-pending {
background-color: #ff9500;
}
&.status-processing {
background-color: #007aff;
}
&.status-completed {
background-color: #34c759;
}
&.status-cancelled {
background-color: #999;
}
&.status-default {
background-color: #999;
}
}
.status-desc {
font-size: 26rpx;
color: #666;
line-height: 1.4;
}
// 通用区域样式
.section-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
margin-bottom: 24rpx;
}
// 商品信息
.product-section {
background-color: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
}
.product-info {
display: flex;
align-items: flex-start;
}
.product-image {
width: 120rpx;
height: 120rpx;
border-radius: 8rpx;
margin-right: 20rpx;
flex-shrink: 0;
}
.product-details {
flex: 1;
display: flex;
flex-direction: column;
}
.product-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
margin-bottom: 8rpx;
line-height: 1.4;
}
.product-spec {
font-size: 26rpx;
color: #666;
margin-bottom: 12rpx;
}
.order-number {
font-size: 24rpx;
color: #999;
}
// 售后信息
.after-sale-section {
background-color: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
}
.info-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
}
.info-label {
font-size: 28rpx;
color: #666;
min-width: 140rpx;
flex-shrink: 0;
}
.info-value {
font-size: 28rpx;
color: #333;
flex: 1;
text-align: right;
}
// 问题描述
.description-section {
background-color: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
}
.description-content {
background-color: #f8f9fa;
border-radius: 8rpx;
padding: 20rpx;
}
.description-text {
font-size: 28rpx;
color: #333;
line-height: 1.6;
}
// 问题图片
.images-section {
background-color: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
}
.images-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16rpx;
}
.problem-image {
width: 100%;
aspect-ratio: 1;
border-radius: 8rpx;
}
// 处理进度
.progress-section {
background-color: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
}
.progress-list {
display: flex;
flex-direction: column;
gap: 30rpx;
}
.progress-item {
display: flex;
align-items: flex-start;
position: relative;
&:not(:last-child)::after {
content: "";
position: absolute;
left: 10rpx;
top: 40rpx;
width: 2rpx;
height: 30rpx;
background-color: #eee;
}
&.completed:not(:last-child)::after {
background-color: #34c759;
}
}
.progress-icon {
margin-right: 20rpx;
flex-shrink: 0;
margin-top: 4rpx;
}
.progress-content {
flex: 1;
display: flex;
flex-direction: column;
}
.progress-title {
font-size: 28rpx;
color: #333;
font-weight: 500;
margin-bottom: 8rpx;
}
.progress-time {
font-size: 24rpx;
color: #999;
margin-bottom: 8rpx;
}
.progress-desc {
font-size: 26rpx;
color: #666;
line-height: 1.4;
}
// 客服回复
.reply-section {
background-color: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
}
.reply-content {
background-color: #f0f8ff;
border-radius: 8rpx;
padding: 20rpx;
}
.reply-text {
font-size: 28rpx;
color: #333;
line-height: 1.6;
margin-bottom: 12rpx;
display: block;
}
.reply-time {
font-size: 24rpx;
color: #999;
}
// 底部操作
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
padding: 20rpx 30rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
border-top: 1rpx solid #eee;
display: flex;
gap: 20rpx;
}
.action-btn {
flex: 1;
height: 80rpx;
border-radius: 12rpx;
font-size: 32rpx;
font-weight: 600;
border: none;
&.secondary {
background-color: #fff;
color: #666;
border: 1rpx solid #ddd;
}
&.primary {
background-color: #007aff;
color: #fff;
}
}
</style>