6 changed files with 3422 additions and 9 deletions
@ -0,0 +1,878 @@ |
|||
<template> |
|||
<view class="aftersale-container"> |
|||
<!-- 订单信息 --> |
|||
<view class="order-section"> |
|||
<view class="section-title">订单信息</view> |
|||
<view class="order-card"> |
|||
<view class="order-header"> |
|||
<text class="order-no">订单号:{{ orderInfo.orderNo }}</text> |
|||
<text class="order-status">{{ getOrderStatusText(orderInfo.status) }}</text> |
|||
</view> |
|||
<view class="supplier-info"> |
|||
<text class="supplier-name">供应商:{{ orderInfo.supplierName }}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 商品信息 --> |
|||
<view class="goods-section"> |
|||
<view class="section-title">选择售后商品</view> |
|||
<view class="goods-list"> |
|||
<view |
|||
class="goods-item" |
|||
v-for="goods in orderInfo.orderGoods" |
|||
:key="goods.id" |
|||
:class="{ 'selected': selectedGoods.includes(goods.id) }" |
|||
@click="toggleGoodsSelect(goods)" |
|||
> |
|||
<view class="goods-checkbox"> |
|||
<view class="checkbox" :class="{ 'checked': selectedGoods.includes(goods.id) }"> |
|||
<text class="checkbox-icon" v-if="selectedGoods.includes(goods.id)">✓</text> |
|||
</view> |
|||
</view> |
|||
<view class="goods-image"> |
|||
<image :src="showImgJdsz(goods.goodsImg)" mode="aspectFill" /> |
|||
</view> |
|||
<view class="goods-info"> |
|||
<view class="goods-name">{{ goods.goodsTitle }}</view> |
|||
<view class="goods-sku" v-if="goods.skuName">{{ goods.skuName }}</view> |
|||
<view class="goods-meta"> |
|||
<text class="goods-price">¥{{ goods.price }}</text> |
|||
<text class="goods-quantity">×{{ goods.num }}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 售后类型 --> |
|||
<view class="type-section"> |
|||
<view class="section-title">售后类型</view> |
|||
<view class="type-list"> |
|||
<view |
|||
class="type-item" |
|||
v-for="type in aftersaleTypes" |
|||
:key="type.value" |
|||
:class="{ 'selected': aftersaleForm.type === type.value }" |
|||
@click="selectType(type.value)" |
|||
> |
|||
<view class="type-radio"> |
|||
<view class="radio" :class="{ 'checked': aftersaleForm.type === type.value }"> |
|||
<view class="radio-dot" v-if="aftersaleForm.type === type.value"></view> |
|||
</view> |
|||
</view> |
|||
<view class="type-info"> |
|||
<text class="type-name">{{ type.name }}</text> |
|||
<text class="type-desc">{{ type.desc }}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 售后原因 --> |
|||
<view class="reason-section"> |
|||
<view class="section-title">售后原因</view> |
|||
<view class="reason-list"> |
|||
<view |
|||
class="reason-item" |
|||
v-for="reason in reasonList" |
|||
:key="reason.value" |
|||
:class="{ 'selected': aftersaleForm.reason === reason.value }" |
|||
@click="selectReason(reason.value)" |
|||
> |
|||
<text class="reason-text">{{ reason.name }}</text> |
|||
<view class="reason-check" v-if="aftersaleForm.reason === reason.value">✓</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 问题描述 --> |
|||
<view class="description-section"> |
|||
<view class="section-title">问题描述</view> |
|||
<textarea |
|||
class="description-input" |
|||
v-model="aftersaleForm.description" |
|||
placeholder="请详细描述遇到的问题,以便我们更好地为您处理" |
|||
maxlength="500" |
|||
/> |
|||
<view class="char-count">{{ aftersaleForm.description.length }}/500</view> |
|||
</view> |
|||
|
|||
<!-- 上传凭证 --> |
|||
<view class="upload-section"> |
|||
<view class="section-title">上传凭证(选填)</view> |
|||
<view class="upload-area"> |
|||
<view class="image-list"> |
|||
<view |
|||
class="image-item" |
|||
v-for="(image, index) in aftersaleForm.images" |
|||
:key="index" |
|||
> |
|||
<image class="uploaded-image" :src="image" mode="aspectFill" /> |
|||
<view class="delete-btn" @click="deleteImage(index)">×</view> |
|||
</view> |
|||
<view class="upload-btn" @click="chooseImage" v-if="aftersaleForm.images.length < 6"> |
|||
<text class="upload-icon">+</text> |
|||
<text class="upload-text">添加图片</text> |
|||
</view> |
|||
</view> |
|||
<view class="upload-tip">最多可上传6张图片,支持jpg、png格式</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 联系方式 --> |
|||
<view class="contact-section"> |
|||
<view class="section-title">联系方式</view> |
|||
<view class="contact-form"> |
|||
<view class="form-item"> |
|||
<text class="form-label">联系人</text> |
|||
<input |
|||
class="form-input" |
|||
v-model="aftersaleForm.contactName" |
|||
placeholder="请输入联系人姓名" |
|||
/> |
|||
</view> |
|||
<view class="form-item"> |
|||
<text class="form-label">联系电话</text> |
|||
<input |
|||
class="form-input" |
|||
v-model="aftersaleForm.contactPhone" |
|||
placeholder="请输入联系电话" |
|||
type="number" |
|||
/> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 底部提交按钮 --> |
|||
<view class="submit-section"> |
|||
<button class="submit-btn" @click="submitAftersale" :disabled="!canSubmit"> |
|||
提交售后申请 |
|||
</button> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
orderId: '', |
|||
orderInfo: { |
|||
orderNo: '', |
|||
status: 0, |
|||
supplierName: '', |
|||
orderGoods: [] |
|||
}, |
|||
selectedGoods: [], // 选中的商品ID列表 |
|||
aftersaleForm: { |
|||
type: 1, // 售后类型:1-退货退款,2-换货,3-仅退款 |
|||
reason: '', // 售后原因 |
|||
description: '', // 问题描述 |
|||
images: [], // 上传的图片 |
|||
contactName: '', // 联系人 |
|||
contactPhone: '' // 联系电话 |
|||
}, |
|||
aftersaleTypes: [ |
|||
{ |
|||
value: 1, |
|||
name: '退货退款', |
|||
desc: '商品有质量问题,需要退货并退款' |
|||
}, |
|||
{ |
|||
value: 2, |
|||
name: '换货', |
|||
desc: '商品有问题,需要更换同款商品' |
|||
}, |
|||
{ |
|||
value: 3, |
|||
name: '仅退款', |
|||
desc: '未收到商品或商品严重损坏' |
|||
} |
|||
], |
|||
reasonList: [ |
|||
{ value: 'quality', name: '商品质量问题' }, |
|||
{ value: 'damage', name: '商品破损' }, |
|||
{ value: 'wrong', name: '发错商品' }, |
|||
{ value: 'description', name: '与描述不符' }, |
|||
{ value: 'logistics', name: '物流问题' }, |
|||
{ value: 'other', name: '其他原因' } |
|||
] |
|||
}; |
|||
}, |
|||
computed: { |
|||
// 是否可以提交 |
|||
canSubmit() { |
|||
return ( |
|||
this.selectedGoods.length > 0 && |
|||
this.aftersaleForm.type && |
|||
this.aftersaleForm.reason && |
|||
this.aftersaleForm.description.trim() && |
|||
this.aftersaleForm.contactName.trim() && |
|||
this.aftersaleForm.contactPhone.trim() |
|||
); |
|||
} |
|||
}, |
|||
onLoad(options) { |
|||
if (options.orderId) { |
|||
this.orderId = options.orderId; |
|||
this.loadOrderInfo(); |
|||
this.loadUserInfo(); |
|||
} |
|||
}, |
|||
methods: { |
|||
showImgJdsz(img) { |
|||
if (!img) return '/static/images/default-goods.png'; |
|||
if (img.indexOf("https://") != -1 || img.indexOf("http://") != -1) { |
|||
return img; |
|||
} else { |
|||
return this.JDSU_IMG_URL + img; |
|||
} |
|||
}, |
|||
|
|||
// 加载订单信息 |
|||
async loadOrderInfo() { |
|||
try { |
|||
this.Post( |
|||
{ orderId: this.orderId }, |
|||
'/framework/haveFeeling/order/detail', |
|||
'DES' |
|||
).then((res) => { |
|||
if (res.code == 200) { |
|||
this.orderInfo = res.data; |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}); |
|||
} catch (error) { |
|||
console.error('加载订单信息失败:', error); |
|||
} |
|||
}, |
|||
|
|||
// 加载用户信息 |
|||
loadUserInfo() { |
|||
const userInfo = uni.getStorageSync('userInfo'); |
|||
if (userInfo) { |
|||
const user = JSON.parse(userInfo); |
|||
this.aftersaleForm.contactName = user.nickName || ''; |
|||
this.aftersaleForm.contactPhone = user.phone || ''; |
|||
} |
|||
}, |
|||
|
|||
// 获取订单状态文本 |
|||
getOrderStatusText(status) { |
|||
const statusMap = { |
|||
"-1": "已取消", |
|||
0: "待支付", |
|||
1: "已支付", |
|||
2: "已发货", |
|||
3: "已完成" |
|||
}; |
|||
return statusMap[status] || "未知"; |
|||
}, |
|||
|
|||
// 切换商品选择 |
|||
toggleGoodsSelect(goods) { |
|||
const index = this.selectedGoods.indexOf(goods.id); |
|||
if (index > -1) { |
|||
this.selectedGoods.splice(index, 1); |
|||
} else { |
|||
this.selectedGoods.push(goods.id); |
|||
} |
|||
}, |
|||
|
|||
// 选择售后类型 |
|||
selectType(type) { |
|||
this.aftersaleForm.type = type; |
|||
}, |
|||
|
|||
// 选择售后原因 |
|||
selectReason(reason) { |
|||
this.aftersaleForm.reason = reason; |
|||
}, |
|||
|
|||
// 选择图片 |
|||
chooseImage() { |
|||
const remainCount = 6 - this.aftersaleForm.images.length; |
|||
uni.chooseImage({ |
|||
count: remainCount, |
|||
sizeType: ['compressed'], |
|||
sourceType: ['album', 'camera'], |
|||
success: (res) => { |
|||
this.uploadImages(res.tempFilePaths); |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
// 上传图片 |
|||
async uploadImages(filePaths) { |
|||
uni.showLoading({ |
|||
title: '上传中...' |
|||
}); |
|||
|
|||
try { |
|||
for (let filePath of filePaths) { |
|||
const uploadResult = await this.uploadSingleImage(filePath); |
|||
if (uploadResult) { |
|||
this.aftersaleForm.images.push(uploadResult); |
|||
} |
|||
} |
|||
} catch (error) { |
|||
console.error('图片上传失败:', error); |
|||
uni.showToast({ |
|||
title: '图片上传失败', |
|||
icon: 'none' |
|||
}); |
|||
} finally { |
|||
uni.hideLoading(); |
|||
} |
|||
}, |
|||
|
|||
// 上传单张图片 |
|||
uploadSingleImage(filePath) { |
|||
return new Promise((resolve, reject) => { |
|||
uni.uploadFile({ |
|||
url: this.BASE_URL + '/framework/upload/image', |
|||
filePath: filePath, |
|||
name: 'file', |
|||
success: (res) => { |
|||
try { |
|||
const data = JSON.parse(res.data); |
|||
if (data.code === 200) { |
|||
resolve(data.data.url); |
|||
} else { |
|||
reject(new Error(data.msg)); |
|||
} |
|||
} catch (error) { |
|||
reject(error); |
|||
} |
|||
}, |
|||
fail: (error) => { |
|||
reject(error); |
|||
} |
|||
}); |
|||
}); |
|||
}, |
|||
|
|||
// 删除图片 |
|||
deleteImage(index) { |
|||
this.aftersaleForm.images.splice(index, 1); |
|||
}, |
|||
|
|||
// 提交售后申请 |
|||
async submitAftersale() { |
|||
if (!this.canSubmit) { |
|||
uni.showToast({ |
|||
title: '请完善售后信息', |
|||
icon: 'none' |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
// 验证联系电话格式 |
|||
const phoneReg = /^1[3-9]\d{9}$/; |
|||
if (!phoneReg.test(this.aftersaleForm.contactPhone)) { |
|||
uni.showToast({ |
|||
title: '请输入正确的手机号', |
|||
icon: 'none' |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
uni.showLoading({ |
|||
title: '提交中...' |
|||
}); |
|||
|
|||
try { |
|||
const params = { |
|||
orderId: this.orderId, |
|||
goodsIds: this.selectedGoods, |
|||
type: this.aftersaleForm.type, |
|||
reason: this.aftersaleForm.reason, |
|||
description: this.aftersaleForm.description, |
|||
images: this.aftersaleForm.images.join(','), |
|||
contactName: this.aftersaleForm.contactName, |
|||
contactPhone: this.aftersaleForm.contactPhone |
|||
}; |
|||
|
|||
this.Post( |
|||
params, |
|||
'/framework/haveFeeling/aftersale/submit', |
|||
'DES' |
|||
).then((res) => { |
|||
uni.hideLoading(); |
|||
if (res.code == 200) { |
|||
uni.showToast({ |
|||
title: '售后申请提交成功', |
|||
icon: 'success' |
|||
}); |
|||
setTimeout(() => { |
|||
uni.navigateBack(); |
|||
}, 1500); |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg || '提交失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}); |
|||
} catch (error) { |
|||
uni.hideLoading(); |
|||
console.error('提交售后申请失败:', error); |
|||
uni.showToast({ |
|||
title: '提交失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.aftersale-container { |
|||
min-height: 100vh; |
|||
background-color: #f5f5f5; |
|||
padding-bottom: 100rpx; |
|||
} |
|||
|
|||
// 通用样式 |
|||
.section-title { |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
// 订单信息 |
|||
.order-section { |
|||
background-color: #fff; |
|||
padding: 30rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.order-card { |
|||
background-color: #f8f9fa; |
|||
border-radius: 12rpx; |
|||
padding: 24rpx; |
|||
} |
|||
|
|||
.order-header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 12rpx; |
|||
} |
|||
|
|||
.order-no { |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.order-status { |
|||
font-size: 24rpx; |
|||
color: #007aff; |
|||
background-color: #e6f3ff; |
|||
padding: 4rpx 12rpx; |
|||
border-radius: 8rpx; |
|||
} |
|||
|
|||
.supplier-info { |
|||
.supplier-name { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
} |
|||
} |
|||
|
|||
// 商品选择 |
|||
.goods-section { |
|||
background-color: #fff; |
|||
padding: 30rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.goods-list { |
|||
.goods-item { |
|||
display: flex; |
|||
align-items: center; |
|||
padding: 20rpx 0; |
|||
border-bottom: 1rpx solid #f0f0f0; |
|||
transition: all 0.3s ease; |
|||
|
|||
&:last-child { |
|||
border-bottom: none; |
|||
} |
|||
|
|||
&.selected { |
|||
background-color: #f0f8ff; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.goods-checkbox { |
|||
margin-right: 20rpx; |
|||
|
|||
.checkbox { |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
border: 2rpx solid #ddd; |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
transition: all 0.3s ease; |
|||
|
|||
&.checked { |
|||
background-color: #007aff; |
|||
border-color: #007aff; |
|||
|
|||
.checkbox-icon { |
|||
color: #fff; |
|||
font-size: 24rpx; |
|||
font-weight: bold; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.goods-image { |
|||
width: 80rpx; |
|||
height: 80rpx; |
|||
border-radius: 8rpx; |
|||
overflow: hidden; |
|||
margin-right: 20rpx; |
|||
|
|||
image { |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
} |
|||
|
|||
.goods-info { |
|||
flex: 1; |
|||
} |
|||
|
|||
.goods-name { |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.goods-sku { |
|||
font-size: 22rpx; |
|||
color: #666; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.goods-meta { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
} |
|||
|
|||
.goods-price { |
|||
font-size: 24rpx; |
|||
color: #f56565; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.goods-quantity { |
|||
font-size: 22rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
// 售后类型 |
|||
.type-section { |
|||
background-color: #fff; |
|||
padding: 30rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.type-list { |
|||
.type-item { |
|||
display: flex; |
|||
align-items: center; |
|||
padding: 24rpx 0; |
|||
border-bottom: 1rpx solid #f0f0f0; |
|||
|
|||
&:last-child { |
|||
border-bottom: none; |
|||
} |
|||
|
|||
&.selected { |
|||
background-color: #f0f8ff; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.type-radio { |
|||
margin-right: 20rpx; |
|||
|
|||
.radio { |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
border: 2rpx solid #ddd; |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
transition: all 0.3s ease; |
|||
|
|||
&.checked { |
|||
border-color: #007aff; |
|||
|
|||
.radio-dot { |
|||
width: 20rpx; |
|||
height: 20rpx; |
|||
background-color: #007aff; |
|||
border-radius: 50%; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.type-info { |
|||
flex: 1; |
|||
} |
|||
|
|||
.type-name { |
|||
display: block; |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
margin-bottom: 6rpx; |
|||
} |
|||
|
|||
.type-desc { |
|||
font-size: 22rpx; |
|||
color: #666; |
|||
line-height: 1.4; |
|||
} |
|||
|
|||
// 售后原因 |
|||
.reason-section { |
|||
background-color: #fff; |
|||
padding: 30rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.reason-list { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
gap: 16rpx; |
|||
} |
|||
|
|||
.reason-item { |
|||
position: relative; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 16rpx 24rpx; |
|||
background-color: #f5f5f5; |
|||
border-radius: 24rpx; |
|||
border: 1rpx solid transparent; |
|||
transition: all 0.3s ease; |
|||
min-width: 140rpx; |
|||
|
|||
&.selected { |
|||
background-color: #e6f3ff; |
|||
border-color: #007aff; |
|||
color: #007aff; |
|||
} |
|||
} |
|||
|
|||
.reason-text { |
|||
font-size: 24rpx; |
|||
color: #333; |
|||
} |
|||
|
|||
.reason-check { |
|||
position: absolute; |
|||
top: -8rpx; |
|||
right: -8rpx; |
|||
width: 24rpx; |
|||
height: 24rpx; |
|||
background-color: #007aff; |
|||
color: #fff; |
|||
border-radius: 50%; |
|||
font-size: 16rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
// 问题描述 |
|||
.description-section { |
|||
background-color: #fff; |
|||
padding: 30rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.description-input { |
|||
width: 100%; |
|||
min-height: 200rpx; |
|||
background-color: #f8f9fa; |
|||
border-radius: 12rpx; |
|||
padding: 20rpx; |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
line-height: 1.6; |
|||
border: none; |
|||
resize: none; |
|||
} |
|||
|
|||
.char-count { |
|||
text-align: right; |
|||
font-size: 22rpx; |
|||
color: #999; |
|||
margin-top: 12rpx; |
|||
} |
|||
|
|||
// 上传凭证 |
|||
.upload-section { |
|||
background-color: #fff; |
|||
padding: 30rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.upload-area { |
|||
.image-list { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
gap: 16rpx; |
|||
margin-bottom: 16rpx; |
|||
} |
|||
|
|||
.image-item { |
|||
position: relative; |
|||
width: 160rpx; |
|||
height: 160rpx; |
|||
border-radius: 12rpx; |
|||
overflow: hidden; |
|||
|
|||
.uploaded-image { |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
|
|||
.delete-btn { |
|||
position: absolute; |
|||
top: -8rpx; |
|||
right: -8rpx; |
|||
width: 32rpx; |
|||
height: 32rpx; |
|||
background-color: #ff4757; |
|||
color: #fff; |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
font-size: 20rpx; |
|||
font-weight: bold; |
|||
} |
|||
} |
|||
|
|||
.upload-btn { |
|||
width: 160rpx; |
|||
height: 160rpx; |
|||
background-color: #f8f9fa; |
|||
border: 2rpx dashed #ddd; |
|||
border-radius: 12rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
color: #666; |
|||
|
|||
.upload-icon { |
|||
font-size: 48rpx; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.upload-text { |
|||
font-size: 22rpx; |
|||
} |
|||
} |
|||
|
|||
.upload-tip { |
|||
font-size: 22rpx; |
|||
color: #999; |
|||
text-align: center; |
|||
} |
|||
} |
|||
|
|||
// 联系方式 |
|||
.contact-section { |
|||
background-color: #fff; |
|||
padding: 30rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.contact-form { |
|||
.form-item { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 24rpx; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
} |
|||
|
|||
.form-label { |
|||
width: 160rpx; |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.form-input { |
|||
flex: 1; |
|||
height: 80rpx; |
|||
background-color: #f8f9fa; |
|||
border-radius: 8rpx; |
|||
padding: 0 20rpx; |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
border: none; |
|||
} |
|||
} |
|||
|
|||
// 提交按钮 |
|||
.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; |
|||
} |
|||
|
|||
.submit-btn { |
|||
width: 100%; |
|||
height: 88rpx; |
|||
background-color: #007aff; |
|||
color: #fff; |
|||
border-radius: 12rpx; |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
border: none; |
|||
transition: all 0.3s ease; |
|||
|
|||
&:disabled { |
|||
background-color: #ccc; |
|||
color: #999; |
|||
} |
|||
|
|||
&:not(:disabled):active { |
|||
transform: scale(0.98); |
|||
} |
|||
} |
|||
</style> |
File diff suppressed because it is too large
@ -0,0 +1,810 @@ |
|||
<template> |
|||
<view class="order-list-container"> |
|||
<!-- Tab切换 --> |
|||
<view class="tab-container"> |
|||
<view |
|||
class="tab-item" |
|||
:class="[{ active: currentTab == index }]" |
|||
v-for="(tab, index) in tabs" |
|||
:key="index" |
|||
@click="switchTab(index)" |
|||
> |
|||
<text class="tab-text">{{ tab.name }}</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 订单列表 --> |
|||
<scroll-view |
|||
class="order-scroll" |
|||
scroll-y |
|||
@scrolltolower="loadMore" |
|||
:show-scrollbar="false" |
|||
enhanced |
|||
> |
|||
<!-- 订单项 --> |
|||
<view |
|||
class="order-item" |
|||
v-for="order in orderList" |
|||
:key="order.id" |
|||
@click="goToOrderDetail(order)" |
|||
> |
|||
<!-- 订单头部 - 供应商信息 --> |
|||
<view class="order-header"> |
|||
<view class="supplier-info"> |
|||
<view class="supplier-name">{{ |
|||
order.supplierName || "默认供应商" |
|||
}}</view> |
|||
<view class="order-number">订单号:{{ order.orderNo }}</view> |
|||
</view> |
|||
<view class="order-status-wrapper"> |
|||
<text class="status-name" :class="[getStatusClass(order.status)]">{{ |
|||
getStatusText(order.status) |
|||
}}</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 商品列表 --> |
|||
<view class="goods-section"> |
|||
<view class="goods-list"> |
|||
<view |
|||
class="goods-item" |
|||
v-for="goods in order.orderItems" |
|||
:key="goods.id" |
|||
> |
|||
<view class="goods-image-container"> |
|||
<image |
|||
v-if="goods.specImage" |
|||
class="goods-image" |
|||
:src="goods.specImage" |
|||
mode="aspectFill" |
|||
/> |
|||
</view> |
|||
<view class="goods-info"> |
|||
<text class="goods-name">{{ goods.goodsName }}</text> |
|||
<view class="goods-specs"> |
|||
<view class="spec-tag"> |
|||
{{ goods.specValueOne }}-{{ goods.specValueTwo }} |
|||
</view> |
|||
</view> |
|||
<view class="goods-meta"> |
|||
<text class="goods-price">¥{{ goods.price }}</text> |
|||
<text class="goods-quantity">×{{ goods.quantity || 1 }}</text> |
|||
</view> |
|||
<!-- 规格信息 --> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 订单底部 --> |
|||
<view class="order-footer"> |
|||
<view class="order-info"> |
|||
<view class="order-total-section"> |
|||
<text class="total-label">实付</text> |
|||
<text class="order-total">¥{{ order.payAmount || 0 }}</text> |
|||
</view> |
|||
<view class="order-time">{{ order.createTime }}</view> |
|||
</view> |
|||
<view class="order-actions"> |
|||
<!-- 基于订单状态的操作按钮 --> |
|||
<button |
|||
class="action-btn" |
|||
@click.stop="handleOrderAction(order, 'pay')" |
|||
v-if="order.status === 0" |
|||
> |
|||
去支付 |
|||
</button> |
|||
<button |
|||
class="action-btn" |
|||
@click.stop="handleOrderAction(order, 'logistics')" |
|||
v-if="[2, 3].includes(order.status)" |
|||
> |
|||
查看物流 |
|||
</button> |
|||
<button |
|||
class="action-btn confirm-btn" |
|||
@click.stop="handleOrderAction(order, 'confirm')" |
|||
v-if="order.status === 2" |
|||
> |
|||
确认收货 |
|||
</button> |
|||
<button |
|||
class="action-btn" |
|||
@click.stop="handleOrderAction(order, 'contact')" |
|||
v-if="[1, 2].includes(order.status)" |
|||
> |
|||
联系供应商 |
|||
</button> |
|||
<button |
|||
class="action-btn secondary-btn" |
|||
@click.stop="handleOrderAction(order, 'aftersale')" |
|||
v-if="order.status === 3" |
|||
> |
|||
申请售后 |
|||
</button> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 空状态 --> |
|||
<view |
|||
class="empty-state" |
|||
v-if="(orderList && orderList.length == 0) || loading" |
|||
> |
|||
<image |
|||
class="empty-image" |
|||
:src=" |
|||
showImg('/uploads/20250808/c16267f9cc2b7a68bf23713b5847987e.png') |
|||
" |
|||
mode="aspectFit" |
|||
/> |
|||
<text class="empty-text">暂无订单</text> |
|||
</view> |
|||
|
|||
<!-- 加载更多 --> |
|||
<view class="load-more" v-if="hasMore && orderList.length > 0"> |
|||
<text class="load-text">{{ |
|||
loading ? "加载中..." : "上拉加载更多" |
|||
}}</text> |
|||
</view> |
|||
|
|||
<!-- 没有更多数据 --> |
|||
<view class="no-more" v-if="!hasMore && orderList.length > 0"> |
|||
<text class="no-more-text">没有更多数据了</text> |
|||
</view> |
|||
</scroll-view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
currentTab: 0, |
|||
tabs: [ |
|||
{ |
|||
name: "全部", |
|||
status: "", |
|||
}, |
|||
{ |
|||
name: "待支付", |
|||
status: 0, |
|||
}, |
|||
{ |
|||
name: "已支付", |
|||
status: 1, |
|||
}, |
|||
{ |
|||
name: "已发货", |
|||
status: 2, |
|||
}, |
|||
{ |
|||
name: "已完成", |
|||
status: 3, |
|||
}, |
|||
], |
|||
orderList: [], |
|||
loading: false, |
|||
refresherTriggered: false, |
|||
hasMore: true, |
|||
currentPage: 1, |
|||
pageSize: 10, |
|||
}; |
|||
}, |
|||
onLoad(e) { |
|||
if (e.status) { |
|||
this.currentTab = e.status; |
|||
} |
|||
}, |
|||
onShow() { |
|||
this.resetList(); |
|||
this.loadOrderList(); |
|||
}, |
|||
methods: { |
|||
showImgJdsz(img) { |
|||
if (!img) return; |
|||
if (img.indexOf("https://") != -1 || img.indexOf("http://") != -1) { |
|||
return img; |
|||
} else { |
|||
return this.JDSU_IMG_URL + img; |
|||
} |
|||
}, |
|||
|
|||
// 切换Tab |
|||
switchTab(index) { |
|||
this.currentTab = index; |
|||
this.resetList(); |
|||
this.loadOrderList(); |
|||
}, |
|||
|
|||
// 重置列表 |
|||
resetList() { |
|||
this.orderList = []; |
|||
this.currentPage = 1; |
|||
this.hasMore = true; |
|||
}, |
|||
|
|||
// 加载订单列表 |
|||
async loadOrderList() { |
|||
if (this.loading || !this.hasMore) return; |
|||
|
|||
this.loading = true; |
|||
try { |
|||
const params = { |
|||
pageNum: this.currentPage, |
|||
pageSize: this.pageSize, |
|||
status: this.tabs[this.currentTab].status, |
|||
}; |
|||
|
|||
this.Post( |
|||
{ |
|||
...params, |
|||
}, |
|||
"/framework/ygOrder/pageList", |
|||
"DES" |
|||
).then((res) => { |
|||
if (res.code == 200) { |
|||
this.orderList.push(...res.rows); |
|||
console.log(this.orderList); |
|||
this.hasMore = res.rows.length === this.pageSize; |
|||
this.currentPage++; |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: "none", |
|||
}); |
|||
} |
|||
}); |
|||
} catch (error) { |
|||
console.error("加载订单列表失败:", error); |
|||
uni.showToast({ |
|||
title: "加载失败", |
|||
icon: "none", |
|||
}); |
|||
} finally { |
|||
this.loading = false; |
|||
this.refresherTriggered = false; |
|||
} |
|||
}, |
|||
|
|||
// 上拉加载更多 |
|||
loadMore() { |
|||
this.loadOrderList(); |
|||
}, |
|||
|
|||
// 下拉刷新 |
|||
onRefresh() { |
|||
this.refresherTriggered = true; |
|||
this.resetList(); |
|||
this.loadOrderList(); |
|||
}, |
|||
|
|||
// 获取订单状态文本 |
|||
getStatusText(status) { |
|||
const statusMap = { |
|||
"-1": "已取消", |
|||
0: "待支付", |
|||
1: "已支付", |
|||
2: "已发货", |
|||
3: "已完成", |
|||
}; |
|||
return statusMap[status] || "未知"; |
|||
}, |
|||
|
|||
// 获取订单状态样式类 |
|||
getStatusClass(status) { |
|||
const classMap = { |
|||
"-1": "status-cancelled", |
|||
0: "status-pending", |
|||
1: "status-paid", |
|||
2: "status-shipping", |
|||
3: "status-completed", |
|||
}; |
|||
return classMap[status] || ""; |
|||
}, |
|||
|
|||
// 处理订单操作 |
|||
handleOrderAction(order, action) { |
|||
switch (action) { |
|||
case "pay": |
|||
this.goToPay(order); |
|||
break; |
|||
case "logistics": |
|||
this.viewLogistics(order); |
|||
break; |
|||
case "confirm": |
|||
this.confirmReceipt(order); |
|||
break; |
|||
case "contact": |
|||
this.contactSupplier(order); |
|||
break; |
|||
case "aftersale": |
|||
this.applyAfterSale(order); |
|||
break; |
|||
} |
|||
}, |
|||
|
|||
// 去支付 |
|||
goToPay(order) { |
|||
this.Post( |
|||
{ |
|||
method: "POST", |
|||
orderNo: order.orderNo, |
|||
fromType: 2, |
|||
payAmount: order.payAmount, |
|||
}, |
|||
"/framework/wxPay/submitShopPurOrder", |
|||
"DES" |
|||
).then((res) => { |
|||
uni.requestPayment({ |
|||
nonceStr: res.data.wxInfo.nonceStr, |
|||
package: res.data.wxInfo.package, |
|||
paySign: res.data.wxInfo.paySign, |
|||
signType: res.data.wxInfo.signType, |
|||
timeStamp: res.data.wxInfo.timeStamp, |
|||
success: () => { |
|||
this.resetList(); |
|||
this.loadOrderList(); |
|||
}, |
|||
fail() { |
|||
// uni.navigateTo({ |
|||
// url: '/subPackages/order/trades' |
|||
// }) |
|||
}, |
|||
}); |
|||
}); |
|||
}, |
|||
|
|||
// 查看物流 |
|||
viewLogistics(order) { |
|||
uni.navigateTo({ |
|||
url: `/subPackages/haveFeeling/logistics?orderId=${order.id}`, |
|||
}); |
|||
}, |
|||
|
|||
// 确认收货 |
|||
confirmReceipt(order) { |
|||
uni.showModal({ |
|||
title: "确认收货", |
|||
content: "确认已收到所有商品吗?", |
|||
success: (res) => { |
|||
if (res.confirm) { |
|||
this.confirmReceiptApi(order.id); |
|||
} |
|||
}, |
|||
}); |
|||
}, |
|||
|
|||
// 确认收货API |
|||
confirmReceiptApi(orderId) { |
|||
uni.showLoading({ |
|||
title: "确认中...", |
|||
}); |
|||
|
|||
this.Post( |
|||
{ |
|||
orderId: orderId, |
|||
}, |
|||
"/framework/haveFeeling/order/confirm", |
|||
"DES" |
|||
).then((res) => { |
|||
uni.hideLoading(); |
|||
if (res.code == 200) { |
|||
uni.showToast({ |
|||
title: "确认收货成功", |
|||
icon: "success", |
|||
}); |
|||
setTimeout(() => { |
|||
this.resetList(); |
|||
this.loadOrderList(); |
|||
}, 800); |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg || "确认收货失败", |
|||
icon: "none", |
|||
}); |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
// 联系供应商 |
|||
contactSupplier(order) { |
|||
uni.showToast({ |
|||
title: "正在联系供应商", |
|||
icon: "none", |
|||
}); |
|||
// 这里可以跳转到聊天页面或者拨打电话 |
|||
}, |
|||
|
|||
// 申请售后 |
|||
applyAfterSale(order) { |
|||
uni.navigateTo({ |
|||
url: `/subPackages/haveFeeling/aftersale?orderId=${order.id}`, |
|||
}); |
|||
}, |
|||
|
|||
// 跳转到订单详情页面 |
|||
goToOrderDetail(order) { |
|||
uni.navigateTo({ |
|||
url: `/subPackages/haveFeeling/detail?id=${order.id}`, |
|||
}); |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
// 主题色彩变量 |
|||
$primary-color: #667eea; |
|||
$secondary-color: #f8f9fa; |
|||
$text-primary: #2d3748; |
|||
$text-secondary: #718096; |
|||
$text-muted: #a0aec0; |
|||
$border-color: #e2e8f0; |
|||
$success-color: #48bb78; |
|||
$warning-color: #ed8936; |
|||
$danger-color: #f56565; |
|||
$bg-light: #f7fafc; |
|||
|
|||
.order-list-container { |
|||
min-height: 100vh; |
|||
background-color: $bg-light; |
|||
} |
|||
|
|||
// Tab样式 |
|||
.tab-container { |
|||
display: flex; |
|||
background-color: #ffffff; |
|||
border-bottom: 1px solid rgba(255, 255, 255, 0.1); |
|||
color: black; |
|||
position: sticky; |
|||
top: 0; |
|||
z-index: 10; |
|||
} |
|||
|
|||
.tab-item { |
|||
flex: 1; |
|||
padding: 32rpx 0; |
|||
text-align: center; |
|||
position: relative; |
|||
transition: all 0.3s ease; |
|||
color: #333333; |
|||
|
|||
&.active { |
|||
.tab-text { |
|||
color: #77f3f9; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
&::after { |
|||
content: ""; |
|||
position: absolute; |
|||
bottom: 0; |
|||
left: 50%; |
|||
transform: translateX(-50%); |
|||
width: 60rpx; |
|||
height: 4rpx; |
|||
background-color: #77f3f9; |
|||
border-radius: 2rpx; |
|||
} |
|||
} |
|||
|
|||
&:not(.active) { |
|||
.tab-text { |
|||
opacity: 1; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.tab-text { |
|||
font-size: 26rpx; |
|||
color: #333333; |
|||
font-weight: 600; |
|||
transition: all 0.3s ease; |
|||
} |
|||
|
|||
// 订单列表样式 |
|||
.order-scroll { |
|||
height: calc(100vh - 120rpx); |
|||
padding: 0 24rpx; |
|||
width: 702rpx; |
|||
} |
|||
|
|||
.order-item { |
|||
background-color: #ffffff; |
|||
border-radius: 20rpx; |
|||
overflow: hidden; |
|||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08); |
|||
border: none; |
|||
transition: all 0.3s ease; |
|||
margin-top: 40rpx; |
|||
|
|||
&:active { |
|||
transform: scale(0.98); |
|||
} |
|||
} |
|||
|
|||
// 订单头部 - 供应商信息 |
|||
.order-header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 24rpx 28rpx 20rpx; |
|||
background: white; |
|||
} |
|||
|
|||
.supplier-info { |
|||
flex: 1; |
|||
} |
|||
|
|||
.supplier-name { |
|||
font-size: 30rpx; |
|||
color: $text-primary; |
|||
font-weight: 600; |
|||
margin-bottom: 8rpx; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
white-space: nowrap; |
|||
max-width: 490rpx; |
|||
} |
|||
|
|||
.order-number { |
|||
font-size: 24rpx; |
|||
color: $text-secondary; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.order-status-wrapper { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.status-name { |
|||
font-size: 26rpx; |
|||
padding: 12rpx 20rpx; |
|||
border-radius: 24rpx; |
|||
font-weight: 600; |
|||
letter-spacing: 0.5rpx; |
|||
|
|||
&.status-pending { |
|||
background: #fff3cd; |
|||
color: #856404; |
|||
} |
|||
|
|||
&.status-paid { |
|||
background: #e6f3ff; |
|||
color: #0c5460; |
|||
} |
|||
|
|||
&.status-cancelled { |
|||
background: #f8d7da; |
|||
color: #721c24; |
|||
} |
|||
|
|||
&.status-shipping { |
|||
background: #d4edda; |
|||
color: #155724; |
|||
} |
|||
|
|||
&.status-completed { |
|||
background: #d1ecf1; |
|||
color: #0c5460; |
|||
} |
|||
} |
|||
|
|||
// 商品部分 |
|||
.goods-section { |
|||
margin: 20rpx 0; |
|||
} |
|||
|
|||
.goods-list { |
|||
padding: 0 20rpx; |
|||
} |
|||
|
|||
.goods-item { |
|||
display: flex; |
|||
align-items: center; |
|||
padding: 20rpx; |
|||
margin-bottom: 12rpx; |
|||
background: #ffffff; |
|||
border-radius: 16rpx; |
|||
border: 1px solid rgba(0, 0, 0, 0.05); |
|||
transition: all 0.3s ease; |
|||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04); |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
} |
|||
|
|||
.goods-image-container { |
|||
position: relative; |
|||
margin-right: 20rpx; |
|||
flex-shrink: 0; |
|||
} |
|||
|
|||
.goods-image { |
|||
width: 100rpx; |
|||
height: 100rpx; |
|||
border-radius: 12rpx; |
|||
} |
|||
|
|||
.goods-info { |
|||
flex: 1; |
|||
margin-right: 20rpx; |
|||
} |
|||
|
|||
.goods-name { |
|||
display: block; |
|||
font-size: 26rpx; |
|||
color: $text-primary; |
|||
font-weight: 600; |
|||
margin-bottom: 12rpx; |
|||
line-height: 1.4; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
white-space: nowrap; |
|||
max-width: 490rpx; |
|||
} |
|||
|
|||
.goods-meta { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 10rpx; |
|||
} |
|||
|
|||
.goods-price { |
|||
font-size: 26rpx; |
|||
color: $danger-color; |
|||
font-weight: 600; |
|||
font-family: "SF Mono", "Monaco", "Cascadia Code", monospace; |
|||
} |
|||
|
|||
.goods-quantity { |
|||
font-size: 22rpx; |
|||
color: $text-muted; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
// 商品规格样式 |
|||
.goods-specs { |
|||
margin-top: 10rpx; |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
gap: 8rpx; |
|||
} |
|||
|
|||
.spec-tag { |
|||
background: #f0f8ff; |
|||
border: 1rpx solid #e6f3ff; |
|||
border-radius: 12rpx; |
|||
padding: 4rpx 12rpx; |
|||
display: inline-block; |
|||
margin-bottom: 6rpx; |
|||
font-size: 22rpx; |
|||
color: #4a90e2; |
|||
font-weight: 500; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
white-space: nowrap; |
|||
max-width: 490rpx; |
|||
} |
|||
|
|||
// 订单底部 |
|||
.order-footer { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 24rpx 28rpx; |
|||
background: white; |
|||
margin-top: 20rpx; |
|||
} |
|||
|
|||
.order-info { |
|||
flex: 1; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 8rpx; |
|||
} |
|||
|
|||
.order-total-section { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 8rpx; |
|||
} |
|||
|
|||
.total-label { |
|||
font-size: 22rpx; |
|||
color: $text-muted; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.order-total { |
|||
font-size: 32rpx; |
|||
color: $danger-color; |
|||
font-weight: 600; |
|||
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", |
|||
"Helvetica Neue", STHeiti, "Microsoft Yahei", Tahoma, Simsun, sans-serif; |
|||
} |
|||
|
|||
.order-time { |
|||
font-size: 22rpx; |
|||
color: $text-muted; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.order-actions { |
|||
display: flex; |
|||
gap: 12rpx; |
|||
flex-direction: column; |
|||
|
|||
.action-btn { |
|||
padding: 8rpx 20rpx; |
|||
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; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
height: 58rpx; |
|||
&:active { |
|||
transform: scale(0.95); |
|||
} |
|||
|
|||
&.confirm-btn { |
|||
background-color: $success-color; |
|||
color: #ffffff; |
|||
} |
|||
|
|||
&.secondary-btn { |
|||
background-color: #f5f5f5; |
|||
color: #666666; |
|||
border: 1rpx solid #ddd; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 空状态 |
|||
.empty-state { |
|||
text-align: center; |
|||
padding: 240rpx 40rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.empty-image { |
|||
width: 240rpx; |
|||
height: 240rpx; |
|||
margin-bottom: 40rpx; |
|||
opacity: 0.8; |
|||
} |
|||
|
|||
.empty-text { |
|||
font-size: 32rpx; |
|||
color: $text-muted; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
// 加载更多 |
|||
.load-more, |
|||
.no-more { |
|||
text-align: center; |
|||
padding: 40rpx; |
|||
} |
|||
|
|||
.load-text, |
|||
.no-more-text { |
|||
font-size: 28rpx; |
|||
color: $text-muted; |
|||
font-weight: 500; |
|||
} |
|||
</style> |
@ -0,0 +1,592 @@ |
|||
<template> |
|||
<view class="logistics-container"> |
|||
<!-- 物流状态头部 --> |
|||
<view class="status-header"> |
|||
<view class="status-info"> |
|||
<view class="status-icon"> |
|||
<image :src="getStatusIcon(logisticsInfo.status)" mode="aspectFit" /> |
|||
</view> |
|||
<view class="status-content"> |
|||
<view class="status-title">{{ getStatusText(logisticsInfo.status) }}</view> |
|||
<view class="status-desc" v-if="logisticsInfo.lastUpdate"> |
|||
最新状态:{{ logisticsInfo.lastUpdate }} |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 快递信息 --> |
|||
<view class="express-section"> |
|||
<view class="section-title">快递信息</view> |
|||
<view class="express-card"> |
|||
<view class="express-item"> |
|||
<text class="express-label">快递公司</text> |
|||
<text class="express-value">{{ logisticsInfo.expressCompany || '--' }}</text> |
|||
</view> |
|||
<view class="express-item"> |
|||
<text class="express-label">快递单号</text> |
|||
<text class="express-value" @longpress="copyExpressNo">{{ logisticsInfo.expressNo || '--' }}</text> |
|||
</view> |
|||
<view class="express-item"> |
|||
<text class="express-label">发货时间</text> |
|||
<text class="express-value">{{ logisticsInfo.shippingTime || '--' }}</text> |
|||
</view> |
|||
<view class="express-item"> |
|||
<text class="express-label">预计送达</text> |
|||
<text class="express-value">{{ logisticsInfo.estimatedTime || '--' }}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 收货地址 --> |
|||
<view class="address-section"> |
|||
<view class="section-title">收货地址</view> |
|||
<view class="address-card"> |
|||
<view class="address-info"> |
|||
<view class="recipient-info"> |
|||
<text class="recipient-name">{{ addressInfo.linkName }}</text> |
|||
<text class="recipient-phone">{{ addressInfo.phone }}</text> |
|||
</view> |
|||
<view class="address-detail"> |
|||
{{ addressInfo.province }}{{ addressInfo.city }}{{ addressInfo.area }}{{ addressInfo.address }} |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 物流轨迹 --> |
|||
<view class="trace-section"> |
|||
<view class="section-title">物流轨迹</view> |
|||
<view class="trace-list"> |
|||
<view |
|||
class="trace-item" |
|||
v-for="(trace, index) in logisticsTrace" |
|||
:key="index" |
|||
:class="{ 'is-current': index === 0 }" |
|||
> |
|||
<view class="trace-time">{{ trace.time }}</view> |
|||
<view class="trace-content"> |
|||
<view class="trace-status">{{ trace.status }}</view> |
|||
<view class="trace-location" v-if="trace.location">{{ trace.location }}</view> |
|||
<view class="trace-remark" v-if="trace.remark">{{ trace.remark }}</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 空状态 --> |
|||
<view class="empty-trace" v-if="!logisticsTrace || logisticsTrace.length === 0"> |
|||
<image class="empty-image" src="/static/images/empty-logistics.png" mode="aspectFit" /> |
|||
<text class="empty-text">暂无物流轨迹信息</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 联系供应商 --> |
|||
<view class="contact-section"> |
|||
<view class="section-title">联系供应商</view> |
|||
<view class="contact-card"> |
|||
<view class="supplier-info"> |
|||
<view class="supplier-name">{{ supplierInfo.name || '默认供应商' }}</view> |
|||
<view class="supplier-phone">联系电话:{{ supplierInfo.phone || '暂无' }}</view> |
|||
</view> |
|||
<button class="contact-btn" @click="contactSupplier"> |
|||
<text>联系供应商</text> |
|||
</button> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 订单操作 --> |
|||
<view class="order-actions"> |
|||
<button |
|||
class="action-btn confirm-btn" |
|||
@click="confirmReceipt" |
|||
v-if="logisticsInfo.status === 3" |
|||
> |
|||
确认收货 |
|||
</button> |
|||
<button |
|||
class="action-btn secondary-btn" |
|||
@click="applyAfterSale" |
|||
v-if="[2, 3, 4].includes(logisticsInfo.status)" |
|||
> |
|||
申请售后 |
|||
</button> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
orderId: '', |
|||
logisticsInfo: { |
|||
status: 0, |
|||
expressCompany: '', |
|||
expressNo: '', |
|||
shippingTime: '', |
|||
estimatedTime: '', |
|||
lastUpdate: '' |
|||
}, |
|||
addressInfo: { |
|||
linkName: '', |
|||
phone: '', |
|||
province: '', |
|||
city: '', |
|||
area: '', |
|||
address: '' |
|||
}, |
|||
supplierInfo: { |
|||
name: '', |
|||
phone: '' |
|||
}, |
|||
logisticsTrace: [] |
|||
}; |
|||
}, |
|||
onLoad(options) { |
|||
if (options.orderId) { |
|||
this.orderId = options.orderId; |
|||
this.loadLogisticsInfo(); |
|||
} |
|||
}, |
|||
methods: { |
|||
// 加载物流信息 |
|||
async loadLogisticsInfo() { |
|||
try { |
|||
uni.showLoading({ |
|||
title: '加载中...' |
|||
}); |
|||
|
|||
this.Post( |
|||
{ orderId: this.orderId }, |
|||
'/framework/haveFeeling/order/logistics', |
|||
'DES' |
|||
).then((res) => { |
|||
if (res.code == 200) { |
|||
this.logisticsInfo = res.data.logistics || {}; |
|||
this.addressInfo = res.data.address || {}; |
|||
this.supplierInfo = res.data.supplier || {}; |
|||
this.logisticsTrace = res.data.trace || []; |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}); |
|||
} catch (error) { |
|||
console.error('加载物流信息失败:', error); |
|||
uni.showToast({ |
|||
title: '加载失败', |
|||
icon: 'none' |
|||
}); |
|||
} finally { |
|||
uni.hideLoading(); |
|||
} |
|||
}, |
|||
|
|||
// 获取状态文本 |
|||
getStatusText(status) { |
|||
const statusMap = { |
|||
0: '待发货', |
|||
1: '已发货', |
|||
2: '运输中', |
|||
3: '派送中', |
|||
4: '已签收', |
|||
5: '异常' |
|||
}; |
|||
return statusMap[status] || '待发货'; |
|||
}, |
|||
|
|||
// 获取状态图标 |
|||
getStatusIcon(status) { |
|||
const iconMap = { |
|||
0: '/static/images/logistics-waiting.png', |
|||
1: '/static/images/logistics-shipped.png', |
|||
2: '/static/images/logistics-transit.png', |
|||
3: '/static/images/logistics-delivering.png', |
|||
4: '/static/images/logistics-completed.png', |
|||
5: '/static/images/logistics-exception.png' |
|||
}; |
|||
return iconMap[status] || '/static/images/logistics-waiting.png'; |
|||
}, |
|||
|
|||
// 复制快递单号 |
|||
copyExpressNo() { |
|||
if (this.logisticsInfo.expressNo) { |
|||
uni.setClipboardData({ |
|||
data: this.logisticsInfo.expressNo, |
|||
success: () => { |
|||
uni.showToast({ |
|||
title: '快递单号已复制', |
|||
icon: 'success' |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
}, |
|||
|
|||
// 联系供应商 |
|||
contactSupplier() { |
|||
if (this.supplierInfo.phone) { |
|||
uni.makePhoneCall({ |
|||
phoneNumber: this.supplierInfo.phone |
|||
}); |
|||
} else { |
|||
uni.showToast({ |
|||
title: '暂无供应商联系方式', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}, |
|||
|
|||
// 确认收货 |
|||
confirmReceipt() { |
|||
uni.showModal({ |
|||
title: '确认收货', |
|||
content: '确认已收到所有商品吗?', |
|||
success: (res) => { |
|||
if (res.confirm) { |
|||
this.confirmReceiptApi(); |
|||
} |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
// 确认收货API |
|||
confirmReceiptApi() { |
|||
uni.showLoading({ |
|||
title: '确认中...' |
|||
}); |
|||
|
|||
this.Post( |
|||
{ orderId: this.orderId }, |
|||
'/framework/haveFeeling/order/confirm', |
|||
'DES' |
|||
).then((res) => { |
|||
uni.hideLoading(); |
|||
if (res.code == 200) { |
|||
uni.showToast({ |
|||
title: '确认收货成功', |
|||
icon: 'success' |
|||
}); |
|||
setTimeout(() => { |
|||
uni.navigateBack(); |
|||
}, 800); |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg || '确认收货失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
// 申请售后 |
|||
applyAfterSale() { |
|||
uni.navigateTo({ |
|||
url: `/subPackages/haveFeeling/aftersale?orderId=${this.orderId}` |
|||
}); |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.logistics-container { |
|||
min-height: 100vh; |
|||
background-color: #f5f5f5; |
|||
padding-bottom: 100rpx; |
|||
} |
|||
|
|||
// 状态头部 |
|||
.status-header { |
|||
background-color: #fff; |
|||
padding: 40rpx 30rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.status-info { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.status-icon { |
|||
width: 80rpx; |
|||
height: 80rpx; |
|||
margin-right: 24rpx; |
|||
|
|||
image { |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
} |
|||
|
|||
.status-content { |
|||
flex: 1; |
|||
} |
|||
|
|||
.status-title { |
|||
font-size: 32rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.status-desc { |
|||
font-size: 26rpx; |
|||
color: #666; |
|||
} |
|||
|
|||
// 通用样式 |
|||
.section-title { |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
// 快递信息 |
|||
.express-section { |
|||
background-color: #fff; |
|||
padding: 30rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.express-card { |
|||
background-color: #f8f9fa; |
|||
border-radius: 12rpx; |
|||
padding: 24rpx; |
|||
} |
|||
|
|||
.express-item { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 20rpx; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
} |
|||
|
|||
.express-label { |
|||
font-size: 26rpx; |
|||
color: #666; |
|||
} |
|||
|
|||
.express-value { |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
// 收货地址 |
|||
.address-section { |
|||
background-color: #fff; |
|||
padding: 30rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.address-card { |
|||
background-color: #f8f9fa; |
|||
border-radius: 12rpx; |
|||
padding: 24rpx; |
|||
} |
|||
|
|||
.recipient-info { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 20rpx; |
|||
margin-bottom: 16rpx; |
|||
} |
|||
|
|||
.recipient-name { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.recipient-phone { |
|||
font-size: 26rpx; |
|||
color: #666; |
|||
} |
|||
|
|||
.address-detail { |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
line-height: 1.5; |
|||
} |
|||
|
|||
// 物流轨迹 |
|||
.trace-section { |
|||
background-color: #fff; |
|||
padding: 30rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.trace-list { |
|||
.trace-item { |
|||
display: flex; |
|||
padding: 24rpx 0; |
|||
border-left: 2rpx solid #e2e8f0; |
|||
padding-left: 40rpx; |
|||
position: relative; |
|||
margin-bottom: 16rpx; |
|||
|
|||
&:before { |
|||
content: ''; |
|||
position: absolute; |
|||
left: -8rpx; |
|||
top: 30rpx; |
|||
width: 14rpx; |
|||
height: 14rpx; |
|||
border-radius: 50%; |
|||
background-color: #e2e8f0; |
|||
} |
|||
|
|||
&.is-current { |
|||
border-left-color: #007aff; |
|||
|
|||
&:before { |
|||
background-color: #007aff; |
|||
box-shadow: 0 0 0 4rpx rgba(0, 122, 255, 0.2); |
|||
} |
|||
|
|||
.trace-time, |
|||
.trace-status { |
|||
color: #007aff; |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
|
|||
&:last-child { |
|||
border-left-color: transparent; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.trace-time { |
|||
font-size: 22rpx; |
|||
color: #999; |
|||
margin-bottom: 8rpx; |
|||
min-width: 120rpx; |
|||
flex-shrink: 0; |
|||
} |
|||
|
|||
.trace-content { |
|||
flex: 1; |
|||
margin-left: 20rpx; |
|||
} |
|||
|
|||
.trace-status { |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
margin-bottom: 6rpx; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.trace-location { |
|||
font-size: 22rpx; |
|||
color: #666; |
|||
margin-bottom: 4rpx; |
|||
} |
|||
|
|||
.trace-remark { |
|||
font-size: 22rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
// 空状态 |
|||
.empty-trace { |
|||
text-align: center; |
|||
padding: 80rpx 40rpx; |
|||
} |
|||
|
|||
.empty-image { |
|||
width: 200rpx; |
|||
height: 200rpx; |
|||
margin-bottom: 30rpx; |
|||
opacity: 0.6; |
|||
} |
|||
|
|||
.empty-text { |
|||
font-size: 28rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
// 联系供应商 |
|||
.contact-section { |
|||
background-color: #fff; |
|||
padding: 30rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.contact-card { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
background-color: #f8f9fa; |
|||
border-radius: 12rpx; |
|||
padding: 24rpx; |
|||
} |
|||
|
|||
.supplier-info { |
|||
flex: 1; |
|||
} |
|||
|
|||
.supplier-name { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.supplier-phone { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
} |
|||
|
|||
.contact-btn { |
|||
padding: 12rpx 24rpx; |
|||
border-radius: 20rpx; |
|||
font-size: 24rpx; |
|||
background-color: #007aff; |
|||
color: #fff; |
|||
border: none; |
|||
} |
|||
|
|||
// 订单操作 |
|||
.order-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: 16rpx; |
|||
} |
|||
|
|||
.action-btn { |
|||
flex: 1; |
|||
padding: 20rpx 0; |
|||
border-radius: 10rpx; |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
border: none; |
|||
text-align: center; |
|||
|
|||
&.confirm-btn { |
|||
background-color: #48bb78; |
|||
color: #fff; |
|||
} |
|||
|
|||
&.secondary-btn { |
|||
background-color: #f5f5f5; |
|||
color: #666; |
|||
border: 1rpx solid #ddd; |
|||
} |
|||
} |
|||
</style> |
Loading…
Reference in new issue