爱简收旧衣按件回收前端代码仓库
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.
 
 
 
 

848 lines
22 KiB

<template>
<view class="container">
<!-- 顶部导航 -->
<view class="nav-bar" :style="{height: (statusBarHeight + 88) + 'rpx', paddingTop: statusBarHeight + 'px'}">
<view class="back" @tap="goBack">
<uni-icons type="left" size="20"></uni-icons>
</view>
<text class="title">{{ orderId }}</text>
</view>
<!-- 内容区域 -->
<view class="content">
<!-- 回收流程卡片 -->
<view class="card process-card">
<view class="card-title">回收流程</view>
<view class="process-steps">
<view
v-for="(step, index) in processSteps"
:key="index"
class="step-item"
:class="{ cancel: currentStep === 0 && index === 1 }"
>
<image :src="step.icon" mode="aspectFit" class="step-icon"></image>
<view
class="step-label"
:class="{
active: currentStep === index + 1,
cancel: currentStep === 0 && index === 1
}"
>
<view class="step-label-inner">
<text class="step-num">{{ ['①','②','③','④'][index] }}</text>
<text class="step-text">{{ step.text }}</text>
</view>
</view>
</view>
</view>
<view class="process-divider"></view>
<!-- 状态信息 -->
<view class="status-info" v-if="currentStatus">
<image class="status-icon" :src="currentStatus.icon" mode="aspectFit"></image>
<view class="status-detail">
<text class="status-text" v-if="currentStep === 0">已取消本次预约</text>
<text class="status-text" v-else>{{ currentStatus.text }}</text>
<text class="status-time">{{ currentStatus.time }}</text>
</view>
</view>
<!-- 取消后提示语 -->
<view v-if="currentStep === 0" class="cancel-tip">期待您下次的支持,共同为地球减少碳排放出一份力!</view>
<!-- 物流公司,仅currentStep为1、2时显示 -->
<view class="express-info" v-if="currentStep !== 0 && currentStep < 3">
<text class="express-label">物流公司</text>
<view class="express-row">
<text class="express-value">{{ expressCompany }}{{ expressNo }}</text>
<text class="express-copy" @tap="copyExpressNo">复制</text>
</view>
</view>
<view class="info-divider" v-if="currentStep !== 0 && currentStep < 4"></view>
<!-- 地址信息,仅非取消和非结款时显示 -->
<view class="pickup-info" v-if="currentStep !== 0 && currentStep < 4">
<view class="info-item" @tap="viewAddress">
<text class="label">取件地址</text>
<view class="value">
<text class="text">{{ address }}</text>
<!-- <text class="arrow">></text> -->
</view>
</view>
<view class="info-item">
<text class="label">{{ timeLabel }}</text>
<view class="value">
<text class="text">{{ appointmentTime }}</text>
<!-- <text class="arrow">></text> -->
</view>
</view>
</view>
</view>
<!-- 订单详情卡片 -->
<view class="order-detail-card">
<text class="order-title">订单详情</text>
<view class="order-row">
<text class="order-label">订单编号</text>
<text class="order-value">{{ orderId }}</text>
</view>
<view class="order-divider"></view>
<template v-if="currentStep < 3">
<view class="order-row">
<text class="order-label">预估回收</text>
<text class="order-value order-highlight">¥ {{ estimatePrice }}</text>
</view>
</template>
<template v-else-if="currentStep === 4">
<view class="order-row">
<text class="order-label">合格结算</text>
<text class="order-value order-highlight">¥ {{ finalPrice }}</text>
</view>
<view class="order-row">
<text class="order-label">运费扣除</text>
<text class="order-value">¥ 0</text>
</view>
<view class="order-row">
<text class="order-label">结算金额</text>
<text class="order-value order-highlight">¥ {{ finalPrice }}</text>
</view>
</template>
<view class="order-divider"></view>
<view class="goods-list">
<view class="goods-item" v-for="(item, index) in clothesList" :key="index">
<image class="goods-img" :src="item.image" mode="aspectFit"></image>
<view class="goods-info">
<text class="goods-name">{{ item.name }}</text>
<text class="goods-desc">{{ item.description }}</text>
<view class="goods-meta">
<text class="goods-price">¥ {{ item.price }}<text class="goods-unit"> /</text></text>
<text class="goods-count">x{{ item.count }}</text>
</view>
</view>
<text class="goods-total">¥{{ item.total }}</text>
</view>
</view>
</view>
<!-- 质检结果卡片仅结款状态显示 -->
<view class="order-detail-card" v-if="currentStep === 4">
<text class="order-title">质检结果</text>
<view class="order-row"><text class="order-label">质检数量</text><text class="order-value">9 件</text></view>
<view class="order-row"><text class="order-label">质检合格</text><text class="order-value">7 件</text></view>
<view class="order-row"><text class="order-label">质量问题</text><text class="order-value">2 件</text></view>
<view class="order-row"><text class="order-label">不可回收</text><text class="order-value">0 件</text></view>
<view class="order-row"><text class="order-label">订单重量</text><text class="order-value">2.85 kg</text></view>
<view class="order-divider"></view>
<view class="report-btn" @tap="viewReport">点此查看质检报告详情</view>
</view>
<!-- 详细信息卡片,仅结款状态显示 -->
<view class="order-detail-card detail-info-card" v-if="currentStep === 4">
<text class="order-title">详细信息</text>
<view class="order-row">
<text class="order-label">预约时间</text>
<text class="order-value">2025-03-20 11:00~12:00</text>
</view>
<view class="order-row">
<text class="order-label">取件地址</text>
<text class="order-value">{{ address }}</text>
</view>
</view>
</view>
<!-- 底部按钮,仅待取件时显示 -->
<view class="bottom-btns" v-if="currentStatus.text === '【待取件】快递员正在赶来'">
<button class="btn cancel-btn" @tap="showCancelModal = true">取消订单</button>
<button class="btn contact-btn" @tap="contactCourier">联系快递员</button>
</view>
<!-- 取消订单弹窗 -->
<view v-if="showCancelModal" class="modal-mask">
<view class="modal-box">
<view class="modal-title">取消订单</view>
<view class="modal-content">确认要取消订单吗</view>
<view class="modal-actions">
<button class="modal-btn modal-cancel" @tap="showCancelModal = false">取消</button>
<button class="modal-btn modal-confirm" @tap="confirmCancelOrder">确认</button>
</view>
</view>
</view>
</view>
</template>
<script>
import pullRefreshMixin from '@/pages/mixins/pullRefreshMixin.js'
export default {
mixins: [pullRefreshMixin],
data() {
return {
orderId: 'RE82738127861525',
currentStep: 2, // 当前进行到第几步
processSteps: [
{ text: '在线预约', icon: '/static/home/① 在线预约.png' },
{ text: '快递上门', icon: '/static/home/② 快递上门.png' },
{ text: '透明质检', icon: '/static/home/③ 透明质检.png' },
{ text: '现金打款', icon: '/static/home/④ 现金打款.png' }
],
address: '海南省海口市秀英区秀英街道5单元',
appointmentTime: '周四 11:00~13:00',
estimatePrice: '73.6-75.8',
finalPrice: '73.6',
clothesList: [
{
name: '羽绒服',
description: '允许脏破烂,160码以上',
price: 8,
count: 8,
total: 64,
image: '/static/home/羽绒服.png'
},
{
name: '品牌羽绒服',
description: '允许脏破烂,160码以上',
price: 10,
count: 1,
total: 10,
image: '/static/home/品牌羽绒服.png'
}
],
currentStatus: {
text: '【待取件】快递员正在赶来',
time: '2025-04-20 11:00~13:00',
icon: '/static/delivery-person.png'
},
hasReport: false,
reportTime: '',
showEditButton: true,
expressCompany: '德邦物流',
expressNo: 'DPK202534653715',
showCancelModal: false,
statusBarHeight: 0,
}
},
computed: {
timeLabel() {
// 根据状态返回不同的时间标签
if (this.currentStep === 4) {
return '回收到账时间'
} else if (this.currentStep === 3) {
return '质检完成时间'
} else {
return '上门时间'
}
},
showEstimate() {
return this.currentStep < 3
}
},
methods: {
async onRefresh() {
// 模拟刷新数据
await new Promise(resolve => setTimeout(resolve, 1000))
this.stopPullRefresh()
},
goBack() {
uni.navigateBack()
},
showMore() {
// 显示更多选项
},
onShare() {
// 分享功能
},
viewAddress() {
// 查看完整地址
},
viewReport() {
if(this.currentStatus.text.includes("已结款") ){
// 查看质检报告
uni.navigateTo({
url: '/pages/component/inspection?status=qualified'
})
}else {
// 查看质检报告
uni.navigateTo({
url: '/pages/component/inspection?status=unqualified'
})
}
},
editOrder() {
// 修改订单
},
// 更新订单状态
updateOrderStatus(status) {
switch(status) {
case 'cancelled':
this.currentStep = 0
this.currentStatus = {
text: '已取消',
time: '',
icon: '/static/cancelled.png'
}
this.showEditButton = false
break
case 'processing':
this.currentStep = 2
this.currentStatus = {
text: '【待取件】快递员正在赶来',
time: '2025-04-20 11:00~13:00',
icon: '/static/delivery-person.png'
}
break
case 'collected':
this.currentStep = 2
this.currentStatus = {
text: '【已取件】快递员正在送至质检',
time: '2025-04-30 11:42',
icon: '/static/collected-truck.png'
}
this.showEditButton = false
break
case 'inspecting':
this.currentStep = 3
this.currentStatus = {
text: '【质检中】质检员正在质检',
time: '2025-04-20 11:00',
icon: '/static/inspector.png'
}
this.showEditButton = false
break
case 'pending_payment':
this.currentStep = 4
this.currentStatus = {
text: '【待结款】待平台确认结款项',
time: '2025-04-20 12:00',
icon: '/static/payment.png'
}
this.hasReport = true
this.reportTime = '2025-03-20 11:40'
this.showEditButton = false
break
case 'completed':
this.currentStep = 4
this.currentStatus = {
text: '【已结款】平台已结款至账户',
time: '2025-04-20 12:01',
icon: '/static/completed.png'
}
this.hasReport = true
this.reportTime = '2025-03-20 11:40'
this.showEditButton = false
break
}
},
copyExpressNo() {
uni.setClipboardData({
data: this.expressCompany + this.expressNo,
success: () => {
uni.showToast({ title: '已复制', icon: 'none' });
}
});
},
confirmCancelOrder() {
this.showCancelModal = false;
// 模拟取消动作
this.currentStep = 0;
this.currentStatus = {
text: '已取消',
time: this.getNowTime(),
icon: '/static/cancelled.png'
};
// uni.showToast({ title: '订单已取消', icon: 'none' });
},
getNowTime() {
const now = new Date();
const y = now.getFullYear();
const m = (now.getMonth() + 1).toString().padStart(2, '0');
const d = now.getDate().toString().padStart(2, '0');
const h = now.getHours().toString().padStart(2, '0');
const min = now.getMinutes().toString().padStart(2, '0');
return `${y}-${m}-${d} ${h}:${min}`;
},
contactCourier() {
// 联系快递员逻辑
uni.showToast({ title: '已联系快递员', icon: 'none' });
}
},
onLoad(options) {
// 接收上一页传来的订单状态
if (options.status) {
this.updateOrderStatus(options.status)
}
this.orderId = options.id
this.statusBarHeight = uni.getSystemInfoSync().statusBarHeight
}
}
</script>
<style scoped lang="scss">
.container {
min-height: 100vh;
background: #f4f4f4;
padding-bottom: 120rpx;
}
.card {
background: linear-gradient(180deg, #fffbe6 0%, #fff 90%);
border-radius: 28rpx;
margin: 24rpx 24rpx 0 24rpx;
box-shadow: 0 8rpx 24rpx rgba(255, 156, 0, 0.03);
padding: 40rpx 32rpx;
}
.nav-bar {
display: flex;
align-items: center;
height: 88rpx;
background: #fff;
padding: 0 30rpx;
.back {
padding: 20rpx;
margin-left: -20rpx;
}
.title {
flex: 1;
text-align: center;
font-size: 34rpx;
font-weight: 500;
}
.right-btns {
display: flex;
align-items: center;
gap: 30rpx;
.more, .target {
font-size: 40rpx;
color: #333;
}
}
}
.card-title, .card-header .title {
font-size: 32rpx;
font-weight: bold;
color: #222;
margin-bottom: 24rpx;
}
.process-card {
background: linear-gradient(180deg, #fffbe6 0%, #fff 35%);
border-radius: 32rpx;
box-shadow: 0 4rpx 16rpx rgba(255, 156, 0, 0.04);
padding: 40rpx 32rpx 32rpx 32rpx;
margin: 24rpx;
}
.card-title {
font-size: 32rpx;
font-weight: bold;
color: #222;
margin-bottom: 24rpx;
text-align: left;
}
.process-steps {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 12rpx;
gap: 18rpx;
}
.step-item {
width: 150%;
height: 150rpx;
background: #fff8ea;
border-radius: 20rpx;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 2rpx 8rpx rgba(255, 156, 0, 0.04);
overflow: hidden;
position: relative;
}
.step-item.cancel {
background: linear-gradient(180deg, #fff0f3 0%, #ffd6df 100%) !important;
}
.step-icon {
width: 86rpx;
height: 86rpx;
margin: 16rpx 0 0 0;
flex-shrink: 0;
}
.step-label {
width: 100%;
height: 44rpx;
background: transparent;
position: absolute;
left: 0;
bottom: 0;
border-radius: 0 0 16rpx 16rpx;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s;
overflow: hidden;
}
.step-label.active {
background: linear-gradient(90deg, #ffd01e 0%, #ff8917 100%);
}
.step-label.cancel {
background: linear-gradient(to right, #ff8e8e 0%, #ff5e5e 100%) !important;
}
.step-label.cancel .step-num,
.step-label.cancel .step-text {
color: #fff !important;
}
.step-label-inner {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
// align-items: center;
justify-content: center;
white-space: nowrap;
}
.step-num,
.step-text {
font-family: PingFang SC;
font-weight: 400;
font-size: 11px;
letter-spacing: 0%;
line-height: 48rpx;
margin: 0;
padding: 0;
color: #9b9b9b;
}
.step-num {
margin-right: 6rpx;
display: flex;
justify-content: center;
align-items: center;
}
.step-label.active .step-num,
.step-label.active .step-text {
font-family: PingFang SC;
font-weight: 500;
font-size: 11px;
letter-spacing: 0%;
color: #fff;
}
.process-divider {
width: 100%;
height: 0;
border-bottom: 1rpx dashed #e6e6e6;
margin: 20rpx 0 0 0;
}
.status-info {
display: flex;
align-items: center;
margin: 24rpx 0;
background: #fafafa;
border-radius: 16rpx;
padding: 16rpx;
}
.status-icon {
width: 60rpx;
height: 60rpx;
margin-right: 16rpx;
}
.status-text {
font-size: 28rpx;
color: #222;
font-weight: bold;
}
.status-time {
font-size: 24rpx;
color: #999;
margin-top: 4rpx;
}
.pickup-info .info-item {
display: flex;
// justify-content: space-between;
flex-direction: column;
// align-items: center;
padding: 18rpx 0;
border-bottom: 1px solid #f0f0f0;
margin: 20rpx 0 20rpx 0;
}
.pickup-info .info-item:last-child {
border-bottom: none;
}
.label {
color: #999;
font-size: 26rpx;
}
.value {
color: #222;
font-size: 26rpx;
display: flex;
align-items: center;
}
.value .arrow {
color: #bbb;
margin-left: 8rpx;
}
.order-detail-card {
background: linear-gradient(to bottom, #fff6e3 0%, #fff 20%);
border-radius: 36rpx;
box-shadow: 0 8rpx 32rpx rgba(255, 156, 0, 0.08);
padding: 40rpx 36rpx 32rpx 36rpx;
margin: 24rpx;
margin-bottom: 164rpx;
}
.order-title {
font-size: 36rpx;
font-weight: bold;
color: #222;
margin-bottom: 32rpx;
text-align: left;
display: block;
}
.order-row {
display: flex;
// justify-content: space-between;
align-items: center;
margin-bottom: 18rpx;
}
.order-label {
color: #bcbcbc;
font-size: 26rpx;
margin: 20rpx 0 20rpx 0;
}
.order-value {
color: #222;
font-size: 28rpx;
margin-left: 30rpx;
font-weight: 500;
}
.order-highlight {
color: #ff9c00;
font-size: 30rpx;
font-weight: bold;
}
.order-divider {
width: 100%;
height: 1rpx;
background: #f0f0f0;
margin: 18rpx 0;
}
.goods-list {
margin-top: 10rpx;
display: flex;
flex-direction: column;
gap: 18rpx;
}
.goods-item {
background: #fff;
border-radius: 24rpx;
display: flex;
align-items: center;
padding: 24rpx 20rpx;
box-shadow: 0 2rpx 8rpx rgba(255, 156, 0, 0.04);
position: relative;
}
.goods-img {
width: 90rpx;
height: 90rpx;
border-radius: 18rpx;
margin-right: 20rpx;
// background: #fffbe6;
flex-shrink: 0;
}
.goods-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
.goods-name {
font-size: 30rpx;
color: #222;
font-weight: bold;
margin-bottom: 6rpx;
}
.goods-desc {
font-size: 24rpx;
color: #bcbcbc;
margin-bottom: 10rpx;
}
.goods-meta {
display: flex;
align-items: center;
gap: 10rpx;
}
.goods-price {
color: #ff9c00;
font-size: 26rpx;
font-weight: bold;
}
.goods-unit {
color: #bcbcbc;
font-size: 22rpx;
font-weight: normal;
}
.goods-count {
color: #bcbcbc;
font-size: 24rpx;
margin-left: 8rpx;
}
.goods-total {
color: #222;
font-size: 30rpx;
font-weight: bold;
margin-left: 18rpx;
flex-shrink: 0;
}
.bottom-btns {
position: fixed;
left: 0;
right: 0;
bottom: 30rpx;
z-index: 10;
background: #fff;
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx 24rpx calc(env(safe-area-inset-bottom) + 12rpx) 24rpx;
box-shadow: 0 -2rpx 8rpx rgba(255, 156, 0, 0.04);
border-radius: 24rpx 24rpx 0 0;
gap: 24rpx;
}
.btn {
flex: 1;
height: 78rpx;
font-size: 32rpx;
font-weight: bold;
border-radius: 44rpx;
margin: 0;
padding: 0;
}
.cancel-btn {
color: #ff9c00;
background: #fff0d2;
border: 2rpx solid #ffd01e;
}
.contact-btn {
color: #fff;
background: linear-gradient(90deg, #ffd01e 0%, #ff8917 100%);
border: none;
}
.express-info {
// border: 1rpx solid #ffdca8;
border-radius: 12rpx;
// padding: 18rpx 20rpx 10rpx 20rpx;
margin-bottom: 12rpx;
margin-top: 8rpx;
background: #fff;
position: relative;
}
.express-label {
color: #bcbcbc;
font-size: 24rpx;
margin-bottom: 8rpx;
display: block;
}
.express-row {
display: flex;
justify-content: space-between;
align-items: center;
}
.express-value {
color: #222;
font-size: 28rpx;
font-weight: 500;
word-break: break-all;
}
.express-copy {
color: #ff9c00;
font-size: 26rpx;
font-weight: 500;
margin-left: 16rpx;
}
.modal-mask {
position: fixed;
left: 0; right: 0; top: 0; bottom: 0;
background: rgba(0,0,0,0.35);
z-index: 999;
display: flex;
align-items: center;
justify-content: center;
}
.modal-box {
width: 560rpx;
background: #fff;
border-radius: 32rpx;
box-shadow: 0 8rpx 32rpx rgba(0,0,0,0.08);
padding: 56rpx 0 0 0;
display: flex;
flex-direction: column;
align-items: center;
}
.modal-title {
font-size: 36rpx;
font-weight: bold;
color: #222;
text-align: center;
margin-bottom: 18rpx;
}
.modal-content {
font-size: 28rpx;
color: #222;
text-align: center;
margin-bottom: 48rpx;
}
.modal-actions {
width: 100%;
display: flex;
border-top: 1rpx solid #f0f0f0;
height: 100rpx;
}
.modal-btn {
flex: 1;
border: none;
outline: none;
background: none;
font-size: 32rpx;
font-weight: 500;
border-radius: 0 0 0 32rpx;
height: 100rpx;
line-height: 100rpx;
}
.modal-cancel {
color: #999;
background: #fff;
border-bottom-left-radius: 32rpx;
}
.modal-confirm {
color: #ff9c00;
background: #fff;
border-bottom-right-radius: 32rpx;
}
.cancel-tip {
color: #bcbcbc;
font-size: 26rpx;
text-align: center;
margin: 24rpx 0 0 0;
}
.info-divider {
width: 100%;
height: 1rpx;
background: #f0f0f0;
margin: 0;
}
.report-btn {
width: 100%;
margin: 24rpx 0 0 0;
padding: 0;
height: 80rpx;
line-height: 80rpx;
text-align: center;
color: #ff9c00;
background: #fff;
border: 2rpx solid #ffd01e;
border-radius: 44rpx;
font-size: 28rpx;
font-weight: bold;
}
.detail-info-card {
margin-bottom: 48rpx;
}
</style>