Browse Source

跳转详情

dev_des
1054425342@qq.com 2 months ago
parent
commit
8e8ab6802e
  1. 24
      pages.json
  2. 2
      pages/index/iSoul.vue
  3. 612
      subPackages/afterSale/add.vue
  4. 632
      subPackages/afterSale/detail.vue
  5. 530
      subPackages/afterSale/list.vue
  6. 18
      subPackages/equityGoods/detail.vue
  7. 304
      subPackages/orderQy/detail.vue
  8. 95
      subPackages/orderQy/list.vue

24
pages.json

@ -254,6 +254,30 @@
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "afterSale/list",
"style": {
"navigationBarTitleText": "售后列表",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "afterSale/add",
"style": {
"navigationBarTitleText": "发起售后",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
"path": "afterSale/detail",
"style": {
"navigationBarTitleText": "售后详情",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
}
]
},

2
pages/index/iSoul.vue

@ -197,7 +197,7 @@
<!-- 底部菜单 -->
<view class="bottom-menu">
<view class="menu-item" v-for="(item, index) in menuItems" :key="index" @click="handleMenuClick(item)">
<text>{{ item.icon }}{{ item.title }}</text>
<text>{{ item.title }}</text>
</view>
</view>

612
subPackages/afterSale/add.vue

@ -0,0 +1,612 @@
<template>
<view class="after-sale-add">
<!-- 商品信息卡片 -->
<view class="card-section">
<view class="card-header">
<text class="card-title">商品信息</text>
</view>
<view class="product-section">
<image
class="product-image"
:src="productInfo.image || '/static/image/default-product.png'"
mode="aspectFill"
/>
<view class="product-info">
<text class="product-title">{{
productInfo.title || "IP文创公仔"
}}</text>
<text class="product-subtitle">{{
productInfo.subtitle || "这里是文创公仔的副标题"
}}</text>
<text class="product-desc">{{
productInfo.description || "介绍"
}}</text>
</view>
</view>
</view>
<!-- 售后表单卡片 -->
<view class="card-section">
<view class="card-header">
<text class="card-title">售后信息</text>
</view>
<view class="form-section">
<!-- 售后类型 -->
<view class="form-item" @click="showTypePopup">
<text class="form-label">售后类型</text>
<view class="form-value-with-icon">
<text class="form-value">{{ afterSaleForm.type }}</text>
<uni-icons type="right" size="16" color="#999" />
</view>
</view>
<!-- 售后原因 -->
<view class="form-item" @click="showReasonPopup">
<text class="form-label">售后原因</text>
<view class="form-value-with-icon">
<text class="form-value">{{ afterSaleForm.reason }}</text>
<uni-icons type="right" size="16" color="#999" />
</view>
</view>
<!-- 理由说明 -->
<view class="form-item">
<text class="form-label">理由说明</text>
<textarea
class="reason-textarea"
v-model="afterSaleForm.description"
placeholder="请详细描述问题..."
maxlength="500"
/>
<text class="char-count"
>{{ afterSaleForm.description.length }}/500</text
>
</view>
<!-- 图片上传 -->
<view class="form-item">
<text class="form-label"
>问题图片 <text class="required">*</text></text
>
<text class="form-sub-label">最多可上传9张图片</text>
<view class="image-upload-section">
<uni-file-picker
v-model="afterSaleForm.images"
fileMediatype="image"
mode="grid"
:limit="9"
@select="onImageSelect"
@delete="onImageDelete"
/>
</view>
</view>
</view>
</view>
<!-- 提交按钮 -->
<view class="submit-section">
<button
class="submit-btn"
@click="submitAfterSale"
:disabled="!canSubmit"
>
提交售后申请
</button>
</view>
<!-- 售后类型选择弹窗 -->
<uni-popup ref="typePopup" type="bottom">
<view class="type-popup">
<view class="popup-header">
<text class="popup-title">选择售后类型</text>
<text class="popup-close" @click="closeTypePopup">×</text>
</view>
<view class="popup-content">
<view
class="type-option"
v-for="(type, index) in typeOptions"
:key="index"
@click="selectType(type)"
>
<view class="type-content">
<text class="type-text">{{ type.text }}</text>
<uni-icons
v-if="afterSaleForm.type === type.text"
type="checkmarkempty"
size="16"
color="#ff4757"
/>
</view>
</view>
</view>
<view class="popup-actions">
<button class="confirm-btn" @click="confirmType">确认</button>
</view>
</view>
</uni-popup>
<!-- 售后原因选择弹窗 -->
<uni-popup ref="reasonPopup" type="bottom">
<view class="reason-popup">
<view class="popup-header">
<text class="popup-title">选择售后原因</text>
<text class="popup-close" @click="closeReasonPopup">×</text>
</view>
<view class="popup-content">
<view
class="reason-option"
v-for="(reason, index) in reasonOptions"
:key="index"
@click="selectReason(reason)"
>
<view class="reason-content">
<text class="reason-text">{{ reason.text }}</text>
<uni-icons
v-if="afterSaleForm.reason === reason.text"
type="checkmarkempty"
size="16"
color="#ff4757"
/>
</view>
</view>
</view>
<view class="popup-actions">
<button class="confirm-btn" @click="confirmReason">确认</button>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
export default {
data() {
return {
productInfo: {
image: "",
title: "IP文创公仔",
subtitle: "这里是文创公仔的副标题",
description: "介绍",
},
afterSaleForm: {
type: "换货",
reason: "商品质量问题",
description: "",
images: [],
},
typeOptions: [
{ text: "换货", value: "exchange" },
{ text: "仅退款", value: "refund" },
{ text: "退货", value: "return" },
],
reasonOptions: [
{ text: "商品质量问题", value: "quality" },
{ text: "物流包装破损", value: "damage" },
{ text: "未收到货", value: "not_received" },
{ text: "商品与描述不符", value: "mismatch" },
{ text: "其他原因", value: "other" },
],
selectedType: null,
selectedReason: null,
};
},
computed: {
canSubmit() {
return (
this.afterSaleForm.description.trim().length > 0 &&
this.afterSaleForm.images.length > 0
);
},
},
onLoad(options) {
//
if (options.productInfo) {
this.productInfo = JSON.parse(decodeURIComponent(options.productInfo));
}
},
methods: {
//
showTypePopup() {
this.$refs.typePopup.open();
},
//
closeTypePopup() {
this.$refs.typePopup.close();
},
//
selectType(type) {
this.selectedType = type;
},
//
confirmType() {
if (this.selectedType) {
this.afterSaleForm.type = this.selectedType.text;
}
this.closeTypePopup();
},
//
showReasonPopup() {
this.$refs.reasonPopup.open();
},
//
closeReasonPopup() {
this.$refs.reasonPopup.close();
},
//
selectReason(reason) {
this.selectedReason = reason;
},
//
confirmReason() {
if (this.selectedReason) {
this.afterSaleForm.reason = this.selectedReason.text;
}
this.closeReasonPopup();
},
//
onImageSelect(e) {
console.log("选择图片:", e);
uni.uploadFile({
url: this.NEWAPIURL_DES + "/system/oss/upload", //
filePath: e.tempFilePaths[0],
name: "file",
success: (uploadFileRes) => {
console.log(uploadFileRes);
let data = JSON.parse(uploadFileRes.data);
if (data.code == 200) {
this.afterSaleForm.images.push(data.url);
}
},
});
},
//
onImageDelete(e) {
console.log("删除图片:", e);
},
//
async submitAfterSale() {
if (!this.canSubmit) {
uni.showToast({
title: "请填写理由说明并上传图片",
icon: "none",
});
return;
}
try {
uni.showLoading({
title: "提交中...",
});
//
const submitData = {
productId: this.productInfo.id,
type: this.afterSaleForm.type,
reason: this.afterSaleForm.reason,
description: this.afterSaleForm.description,
images: this.afterSaleForm.images.map(
(item) => item.url || item.path
),
};
// API
this.Post(submitData, "/framework/afterSale/create", "DES")
.then((res) => {
uni.hideLoading();
if (res.code === 200) {
uni.showToast({
title: "售后申请提交成功",
icon: "success",
});
//
setTimeout(() => {
uni.redirectTo({
url: "/subPackages/afterSale/list",
});
}, 1500);
} 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",
});
}
},
},
};
</script>
<style lang="scss" scoped>
.after-sale-add {
min-height: 100vh;
background-color: #f8f9fa;
padding: 16rpx;
padding-bottom: calc(120rpx + env(safe-area-inset-bottom));
padding-bottom: calc(120rpx + constant(safe-area-inset-bottom));
}
//
.card-section {
background-color: #fff;
border-radius: 16rpx;
margin-bottom: 16rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
overflow: hidden;
}
.card-header {
padding: 20rpx 24rpx;
border-bottom: 1rpx solid #f0f0f0;
background-color: #fafafa;
}
.card-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
}
//
.product-section {
padding: 24rpx;
display: flex;
align-items: flex-start;
}
.product-image {
width: 120rpx;
height: 120rpx;
border-radius: 12rpx;
background-color: #f0f0f0;
margin-right: 20rpx;
flex-shrink: 0;
}
.product-info {
flex: 1;
display: flex;
flex-direction: column;
}
.product-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 6rpx;
}
.product-subtitle {
font-size: 26rpx;
color: #666;
margin-bottom: 4rpx;
}
.product-desc {
font-size: 24rpx;
color: #999;
}
//
.form-section {
padding: 0;
}
.form-item {
padding: 20rpx 24rpx;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
}
.form-label {
font-size: 28rpx;
color: #333;
font-weight: 500;
margin-bottom: 12rpx;
display: block;
}
.required {
color: #ff4757;
font-weight: bold;
}
.form-sub-label {
font-size: 24rpx;
color: #999;
margin-bottom: 12rpx;
display: block;
}
.form-value {
font-size: 28rpx;
color: #333;
}
.form-value-with-icon {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12rpx 0;
}
//
.reason-textarea {
width: 100%;
min-height: 120rpx;
max-height: 200rpx;
padding: 16rpx;
border: 1rpx solid #e0e0e0;
border-radius: 12rpx;
font-size: 28rpx;
color: #333;
background-color: #fafafa;
box-sizing: border-box;
resize: none;
}
.char-count {
font-size: 24rpx;
color: #999;
text-align: right;
margin-top: 8rpx;
display: block;
}
//
.image-upload-section {
margin-top: 12rpx;
}
//
.submit-section {
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;
box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.08);
}
.submit-btn {
width: 100%;
height: 80rpx;
background-color: #ff4757;
color: #fff;
border: none;
border-radius: 12rpx;
font-size: 32rpx;
font-weight: 600;
&:disabled {
background-color: #ccc;
}
}
//
.type-popup {
background-color: #fff;
border-radius: 20rpx 20rpx 0 0;
overflow: hidden;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.popup-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.popup-close {
font-size: 40rpx;
color: #999;
}
.popup-content {
max-height: 600rpx;
overflow-y: auto;
}
.type-option {
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
}
.type-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.type-text {
font-size: 28rpx;
color: #333;
}
.popup-actions {
padding: 30rpx;
border-top: 1rpx solid #f0f0f0;
}
.confirm-btn {
width: 100%;
height: 80rpx;
background-color: #ff4757;
color: #fff;
border: none;
border-radius: 12rpx;
font-size: 32rpx;
font-weight: 600;
}
//
.reason-popup {
background-color: #fff;
border-radius: 20rpx 20rpx 0 0;
overflow: hidden;
}
.reason-option {
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
}
.reason-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.reason-text {
font-size: 28rpx;
color: #333;
}
</style>

632
subPackages/afterSale/detail.vue

@ -0,0 +1,632 @@
<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>

530
subPackages/afterSale/list.vue

@ -0,0 +1,530 @@
<template>
<view class="after-sale-list">
<!-- 状态筛选 -->
<view class="filter-section">
<view class="filter-tabs">
<view
class="filter-tab"
:class="{ active: currentStatus === item.value }"
v-for="item in statusOptions"
:key="item.value"
@click="changeStatus(item.value)"
>
<text class="tab-text">{{ item.text }}</text>
</view>
</view>
</view>
<!-- 售后列表 -->
<scroll-view
class="list-scroll"
scroll-y
@scrolltolower="loadMore"
@refresherrefresh="onRefresh"
:refresher-enabled="true"
:refresher-triggered="refresherTriggered"
>
<view class="list-content">
<view
class="after-sale-item"
v-for="item in afterSaleList"
:key="item.id"
@click="goToDetail(item)"
>
<!-- 商品信息 -->
<view class="product-info">
<image
class="product-image"
:src="item.productImage"
mode="aspectFill"
/>
<view class="product-details">
<text class="product-title">{{ item.productTitle }}</text>
<text class="product-spec">{{ item.productSpec }}</text>
<view class="product-meta">
<text class="order-number">订单号{{ item.orderNumber }}</text>
<text class="create-time">{{
formatTime(item.createTime)
}}</text>
</view>
</view>
</view>
<!-- 售后信息 -->
<view class="after-sale-info">
<view class="info-row">
<text class="info-label">售后类型</text>
<text class="info-value">{{ item.type }}</text>
</view>
<view class="info-row">
<text class="info-label">售后原因</text>
<text class="info-value">{{ item.reason }}</text>
</view>
<view class="info-row">
<text class="info-label">申请时间</text>
<text class="info-value">{{ formatTime(item.createTime) }}</text>
</view>
</view>
<!-- 状态和操作 -->
<view class="status-section">
<view class="status-badge" :class="[getStatusClass(item.status)]">
{{ getStatusText(item.status) }}
</view>
<view class="action-buttons">
<button
class="action-btn"
@click.stop="cancelAfterSale(item)"
v-if="item.status === 'pending'"
>
取消申请
</button>
<button class="action-btn primary" @click.stop="goToDetail(item)">
查看详情
</button>
</view>
</view>
</view>
<!-- 加载更多 -->
<view class="load-more" v-if="hasMore">
<text class="load-text">加载中...</text>
</view>
<view class="no-more" v-else>
<text class="no-more-text">没有更多数据了</text>
</view>
<!-- 空状态 -->
<view class="empty-state" v-if="afterSaleList.length === 0 && !loading">
<image
class="empty-image"
:src="showImg('/uploads/20250808/c16267f9cc2b7a68bf23713b5847987e.png')"
mode="aspectFit"
/>
<text class="empty-text">暂无售后记录</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
currentStatus: "all",
statusOptions: [
{ text: "全部", value: "all" },
{ text: "待处理", value: "pending" },
{ text: "处理中", value: "processing" },
{ text: "已完成", value: "completed" },
{ text: "已取消", value: "cancelled" },
],
afterSaleList: [],
page: 1,
pageSize: 10,
hasMore: true,
loading: false,
refresherTriggered: false,
};
},
onLoad() {
this.loadAfterSaleList();
},
onShow() {
//
this.refreshList();
},
methods: {
//
changeStatus(status) {
if (this.currentStatus === status) return;
this.currentStatus = status;
this.resetList();
this.loadAfterSaleList();
},
//
resetList() {
this.afterSaleList = [];
this.page = 1;
this.hasMore = true;
},
//
async loadAfterSaleList() {
if (this.loading || !this.hasMore) return;
try {
this.loading = true;
const params = {
page: this.page,
pageSize: this.pageSize,
status: this.currentStatus === "all" ? "" : this.currentStatus,
};
this.Post(params, "/framework/afterSale/list", "DES")
.then((res) => {
this.loading = false;
if (res.code === 200) {
const newList = res.data.list || [];
if (this.page === 1) {
this.afterSaleList = newList;
} else {
this.afterSaleList.push(...newList);
}
this.hasMore = newList.length === this.pageSize;
this.page++;
} else {
uni.showToast({
title: res.msg || "加载失败",
icon: "none",
});
}
})
.catch((error) => {
this.loading = false;
console.error("加载售后列表失败:", error);
uni.showToast({
title: "加载失败",
icon: "none",
});
});
} catch (error) {
this.loading = false;
console.error("加载售后列表失败:", error);
uni.showToast({
title: "加载失败",
icon: "none",
});
}
},
//
loadMore() {
this.loadAfterSaleList();
},
//
onRefresh() {
this.refresherTriggered = true;
this.resetList();
this.loadAfterSaleList().finally(() => {
this.refresherTriggered = false;
});
},
//
refreshList() {
this.resetList();
this.loadAfterSaleList();
},
//
goToDetail(item) {
uni.navigateTo({
url: `/subPackages/afterSale/detail?id=${item.id}`,
});
},
//
cancelAfterSale(item) {
uni.showModal({
title: "确认取消",
content: "确定要取消这个售后申请吗?",
success: (res) => {
if (res.confirm) {
this.Post({ id: item.id }, "/framework/afterSale/cancel", "DES")
.then((res) => {
if (res.code === 200) {
uni.showToast({
title: "取消成功",
icon: "success",
});
this.refreshList();
} else {
uni.showToast({
title: res.msg || "取消失败",
icon: "none",
});
}
})
.catch((error) => {
console.error("取消售后申请失败:", error);
uni.showToast({
title: "取消失败",
icon: "none",
});
});
}
},
});
},
//
getStatusText(status) {
const statusMap = {
pending: "待处理",
processing: "处理中",
completed: "已完成",
cancelled: "已取消",
};
return statusMap[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-list {
min-height: 100vh;
background-color: #f5f5f5;
}
//
.filter-section {
background-color: #fff;
padding: 20rpx 0;
margin-bottom: 20rpx;
}
.filter-tabs {
display: flex;
justify-content: space-around;
padding: 0 30rpx;
}
.filter-tab {
padding: 16rpx 24rpx;
border-radius: 20rpx;
transition: all 0.3s ease;
&.active {
background-color: #007aff;
.tab-text {
color: #fff;
}
}
}
.tab-text {
font-size: 28rpx;
color: #666;
font-weight: 500;
}
//
.list-scroll {
height: calc(100vh - 120rpx);
}
.list-content {
padding: 0 20rpx;
}
.after-sale-item {
background-color: #fff;
border-radius: 12rpx;
margin-bottom: 20rpx;
padding: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
}
//
.product-info {
display: flex;
align-items: flex-start;
margin-bottom: 24rpx;
}
.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;
}
.product-meta {
display: flex;
flex-direction: column;
gap: 4rpx;
}
.order-number {
font-size: 24rpx;
color: #999;
}
.create-time {
font-size: 24rpx;
color: #999;
}
//
.after-sale-info {
background-color: #f8f9fa;
border-radius: 8rpx;
padding: 20rpx;
margin-bottom: 24rpx;
}
.info-row {
display: flex;
align-items: center;
margin-bottom: 12rpx;
&:last-child {
margin-bottom: 0;
}
}
.info-label {
font-size: 26rpx;
color: #666;
min-width: 140rpx;
flex-shrink: 0;
}
.info-value {
font-size: 26rpx;
color: #333;
flex: 1;
}
//
.status-section {
display: flex;
justify-content: space-between;
align-items: center;
}
.status-badge {
padding: 8rpx 16rpx;
border-radius: 16rpx;
font-size: 24rpx;
color: #fff;
font-weight: 500;
&.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;
}
}
.action-buttons {
display: flex;
gap: 16rpx;
}
.action-btn {
padding: 12rpx 24rpx;
border-radius: 20rpx;
font-size: 24rpx;
border: 1rpx solid #ddd;
background-color: #fff;
color: #666;
min-width: 120rpx;
&.primary {
background-color: #007aff;
color: #fff;
border-color: #007aff;
}
}
//
.load-more,
.no-more {
text-align: center;
padding: 30rpx;
}
.load-text,
.no-more-text {
font-size: 26rpx;
color: #999;
}
//
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-image {
width: 200rpx;
height: 200rpx;
margin-bottom: 30rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
</style>

18
subPackages/equityGoods/detail.vue

@ -279,11 +279,19 @@ export default {
},
//
handlePurchase() {
console.log("立即购买");
uni.showToast({
title: "(暂未开放)为您跳转到君道苏州下单~",
icon: "none",
});
let id = this.goodsInfo.sku&&this.goodsInfo.sku.product.sceneId
wx.navigateToMiniProgram({
appId: 'wx4bb7b6050831f585',
path: 'pages/info/sceneProductInfo/index?id='+id,
envVersion: process.env.NODE_ENV === 'development'?'trial':'release',
success(res) {
//
},
fail(e){
console.log(e)
}
})
//
},

304
subPackages/orderQy/detail.vue

@ -79,7 +79,7 @@
</button>
<button
v-if="goods.type == 2 && goods.status != 1"
class="action-btn "
class="action-btn"
@click="showLogisticsInfo(goods)"
>
查看物流
@ -176,14 +176,20 @@
<view class="bottom-placeholder"></view>
<!-- 底部操作 -->
<!-- <view class="bottom-actions">
<button class="action-btn secondary" @click="goBack">
{{ orderDetail.status === 0 ? "申请售后" : "返回" }}
</button>
<button class="action-btn primary" @click="handleMainAction">
<view class="bottom-actions">
<!-- <button class="action-btn-bottom primary" @click="handleMainAction">
{{ getMainActionText() }}
</button> -->
<!-- 售后按钮 -->
<button
class="action-btn-bottom after-sale-btn"
@click="handleShowAfterSalePopup"
v-if="orderDetail.status !== 0"
>
售后
</button>
</view> -->
</view>
<!-- 权益码弹窗 -->
<view
@ -308,13 +314,81 @@
currentGoodsInfo.deliveryUserName || "--"
}}</text>
</view>
<view class="info-item">
<text class="info-label">收货时间</text>
<text class="info-value">{{
currentGoodsInfo.completeTime || "--"
}}</text>
</view>
<view class="info-item">
<text class="info-label">收货时间</text>
<text class="info-value">{{
currentGoodsInfo.completeTime || "--"
}}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 售后商品选择弹窗 -->
<view
class="after-sale-popup-mask"
v-if="showAfterSalePopup"
@click="closeAfterSalePopup"
>
<view class="after-sale-popup" @click.stop>
<view class="popup-header">
<text class="popup-title">选择售后商品</text>
<text class="popup-close" @click="closeAfterSalePopup">×</text>
</view>
<view class="popup-content">
<view class="goods-list">
<view
class="goods-select-item"
v-for="goods in orderDetail.orderChildVos"
:key="goods.id"
@click="selectGoodsForAfterSale(goods)"
>
<view class="goods-select-info">
<image
class="goods-select-image"
:src="showImgJdsz(goods.goodsImg.split(',')[0])"
mode="aspectFill"
/>
<view class="goods-select-details">
<text class="goods-select-name">{{
goods.goodsTitle || "-"
}}</text>
<!-- 规格信息 - 当type为2时显示 -->
<view
class="goods-select-specs"
v-if="
goods.type === 2 &&
goods.orderExchangeVo &&
goods.orderExchangeVo.orderExchangeDetailVos
"
>
<view
class="spec-tag"
v-for="(item, index) in goods.orderExchangeVo
.orderExchangeDetailVos"
:key="index"
>
{{ item.specValueOne }} / {{ item.specValueTwo }}
</view>
</view>
<!-- 类型和数量信息 -->
<view class="goods-select-info-row">
<text class="goods-select-type">{{
getGoodsTypeName(goods.type)
}}</text>
<text class="goods-select-quantity"
>数量{{ goods.num || 1 }}</text
>
</view>
</view>
</view>
<view class="goods-select-arrow">
<uni-icons type="right" size="16" color="#999" />
</view>
</view>
</view>
</view>
</view>
@ -334,6 +408,7 @@ export default {
orderDetail: {
orderChildVos: [],
},
showAfterSalePopup: false, //
};
},
computed: {
@ -391,7 +466,7 @@ export default {
0: "/static/icon/status-pending.png",
1: "/static/icon/status-waiting.png",
2: "/static/icon/status-shipping.png",
3: "/static/icon/status-completed.png",
3: "static/icon/status-completed.png",
4: "/static/icon/status-refund.png",
};
return iconMap[status] || "/static/icon/status-default.png";
@ -720,9 +795,9 @@ export default {
icon: "success",
});
//
setTimeout(() =>{
this.loadOrderDetail();
},800)
setTimeout(() => {
this.loadOrderDetail();
}, 800);
} else {
uni.showToast({
title: res.msg || "确认收货失败",
@ -742,6 +817,37 @@ export default {
},
});
},
//
handleShowAfterSalePopup() {
this.showAfterSalePopup = true;
},
//
closeAfterSalePopup() {
this.showAfterSalePopup = false;
},
//
selectGoodsForAfterSale(goods) {
this.currentGoodsInfo = goods; //
this.showAfterSalePopup = false; //
//
const productInfo = {
id: goods.goodsId,
image: this.showImgJdsz(goods.goodsImg.split(",")[0]),
title: goods.goodsTitle,
subtitle: goods.skuName || "",
description: this.getGoodsTypeName(goods.type),
};
uni.navigateTo({
url: `/subPackages/afterSale/add?productInfo=${encodeURIComponent(
JSON.stringify(productInfo)
)}`,
});
},
},
};
</script>
@ -984,6 +1090,21 @@ export default {
height: 55rpx;
line-height: 55rpx;
}
.action-btn-bottom {
padding: 20rpx 24rpx;
border-radius: 20rpx;
font-size: 24rpx;
border: none;
border-radius: 10rpx;
font-size: 24rpx;
font-weight: 600;
border: none;
color: #333333;
transition: all 0.3s ease;
min-width: 100rpx;
text-align: center;
background-color: #77f3f9;
}
.confirm-btn {
background-color: #34c759;
color: #ffffff;
@ -1087,9 +1208,9 @@ export default {
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
border-top: 1rpx solid #eee;
display: flex;
gap: 20rpx;
gap: 16rpx;
.action-btn {
.action-btn-bottom {
flex: 1;
padding: 0rpx 20rpx;
border-radius: 10rpx;
@ -1103,6 +1224,11 @@ export default {
background-color: #77f3f9;
height: 55rpx;
line-height: 55rpx;
&.after-sale-btn {
background-color: #ff4757;
color: #ffffff;
}
}
}
@ -1340,4 +1466,142 @@ export default {
color: #4a90e2;
font-weight: 500;
}
//
.after-sale-popup-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 9999;
display: flex;
align-items: flex-end;
justify-content: center;
}
.after-sale-popup {
width: 100%;
max-height: 70vh;
background-color: #fff;
border-radius: 20rpx 20rpx 0 0;
overflow: hidden;
display: flex;
flex-direction: column;
}
.after-sale-popup .popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1px solid #f0f0f0;
}
.after-sale-popup .popup-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.after-sale-popup .popup-close {
font-size: 40rpx;
color: #999;
}
.after-sale-popup .popup-content {
padding: 0;
flex: 1;
overflow-y: auto;
}
.goods-list {
padding: 0 30rpx 30rpx;
}
.goods-select-item {
display: flex;
align-items: center;
padding: 24rpx 0;
border-bottom: 1rpx solid #f0f0f0;
cursor: pointer;
&:last-child {
border-bottom: none;
}
&:active {
background-color: #f8f9fa;
}
}
.goods-select-info {
display: flex;
align-items: center;
flex: 1;
}
.goods-select-image {
width: 100rpx;
height: 100rpx;
border-radius: 10rpx;
margin-right: 20rpx;
}
.goods-select-details {
flex: 1;
display: flex;
flex-direction: column;
gap: 12rpx;
}
.goods-select-name {
text-align: left;
font-size: 28rpx;
color: #333;
font-weight: 600;
line-height: 1.4;
margin-bottom: 0;
}
.goods-select-specs {
display: flex;
flex-wrap: wrap;
gap: 8rpx;
}
.spec-tag {
background: #f0f8ff;
border: 1rpx solid #e6f3ff;
border-radius: 12rpx;
padding: 4rpx 12rpx;
display: inline-block;
font-size: 22rpx;
color: #4a90e2;
font-weight: 500;
}
.goods-select-info-row {
display: flex;
align-items: center;
gap: 16rpx;
}
.goods-select-type {
font-size: 22rpx;
color: #999;
background-color: #f5f5f5;
padding: 4rpx 8rpx;
border-radius: 6rpx;
}
.goods-select-quantity {
font-size: 22rpx;
color: #999;
}
.goods-select-arrow {
margin-left: 20rpx;
}
</style>

95
subPackages/orderQy/list.vue

@ -48,28 +48,47 @@
<image
v-if="goods.goodsImg"
class="goods-image"
:src="showImgJdsz(goods.goodsImg&&goods.goodsImg.split(',')[0])"
:src="
showImgJdsz(goods.goodsImg && goods.goodsImg.split(',')[0])
"
mode="aspectFill"
/>
<!-- 当商品类型为2时显示状态标签 -->
<view v-if="goods.type === 2" class="goods-status-badge" :class="[getGoodsStatusClass(goods.status)]">
<view
v-if="goods.type === 2"
class="goods-status-badge"
:class="[getGoodsStatusClass(goods.status)]"
>
{{ getGoodsStatusText(goods.status) }}
</view>
</view>
<view class="goods-info">
<text class="goods-name">{{ goods.goodsTitle }}</text>
<text class="goods-name" v-if="goods.skuName">{{ goods.skuName }}</text>
<text class="goods-name" v-if="goods.skuName">{{
goods.skuName
}}</text>
<view class="goods-meta">
<text class="goods-type">{{
getGoodsTypeName(goods.type)
}}</text>
<text class="goods-quantity">数量{{ goods.num || 1 }}</text>
</view>
<view class="goods-specs" v-if="goods.orderExchangeVo && goods.orderExchangeVo.orderExchangeDetailVos">
<view class="spec-tag" v-for="(item, index) in goods.orderExchangeVo.orderExchangeDetailVos" :key="index">
{{item.specValueOne}} / {{item.specValueTwo}}
</view>
</view>
<view
class="goods-specs"
v-if="
goods.orderExchangeVo &&
goods.orderExchangeVo.orderExchangeDetailVos
"
>
<view
class="spec-tag"
v-for="(item, index) in goods.orderExchangeVo
.orderExchangeDetailVos"
:key="index"
>
{{ item.specValueOne }} / {{ item.specValueTwo }}
</view>
</view>
</view>
<view class="goods-action">
<!-- 未使用 -->
@ -81,18 +100,16 @@
>
{{ getActionBtnText(goods.type) }}
</button>
<template v-else>
<button
v-if="!(goods.type==2&&goods.status==2)"
class="action-btn"
:class="[getActionBtnClass(goods.status)]"
@click.stop="handleGoodsAction(goods)"
>
{{ getActionBtnTexted(goods.type) }}
</button>
</template>
<template v-else>
<button
v-if="!(goods.type == 2 && goods.status == 2)"
class="action-btn"
:class="[getActionBtnClass(goods.status)]"
@click.stop="handleGoodsAction(goods)"
>
{{ getActionBtnTexted(goods.type) }}
</button>
</template>
</view>
</view>
</view>
@ -258,7 +275,7 @@ export default {
).then((res) => {
if (res.code == 200) {
this.orderList.push(...res.rows);
console.log(this.orderList)
console.log(this.orderList);
this.hasMore = res.rows.length === this.pageSize;
this.currentPage++;
} else {
@ -402,23 +419,22 @@ export default {
icon: "none",
});
uni.navigateTo({
url: "/subPackages/memorialAlbum/detail?id="+goods.childId,
url: "/subPackages/memorialAlbum/detail?id=" + goods.childId,
});
},
//
reserveDelivery(goods) {
//
if(goods.status==1){
uni.navigateTo({
url: `/subPackages/orderQy/confrim?goodsId=${goods.goodsId}&orderChildId=${goods.childId}`,
});
}else{
uni.navigateTo({
url: `/subPackages/orderQy/detail?id=${goods.orderId}`,
});
}
if (goods.status == 1) {
uni.navigateTo({
url: `/subPackages/orderQy/confrim?goodsId=${goods.goodsId}&orderChildId=${goods.childId}`,
});
} else {
uni.navigateTo({
url: `/subPackages/orderQy/detail?id=${goods.orderId}`,
});
}
},
// 使
@ -463,7 +479,6 @@ export default {
},
});
},
},
};
</script>
@ -719,27 +734,27 @@ $bg-light: #f7fafc;
color: #fff;
font-weight: 500;
z-index: 10;
&.goods-status-waiting {
background-color: #007aff;
}
&.goods-status-pending {
background-color: #ff9500;
}
&.goods-status-shipping {
background-color: #34c759;
}
&.goods-status-completed {
background-color: #666;
}
&.goods-status-refund {
background-color: #ff3b30;
}
&.goods-status-default {
background-color: #999;
}
@ -798,8 +813,6 @@ $bg-light: #f7fafc;
font-weight: 500;
}
.goods-quantity {
font-size: 22rpx;
color: $text-muted;

Loading…
Cancel
Save