6 changed files with 888 additions and 9 deletions
@ -0,0 +1,471 @@ |
|||||
|
<template> |
||||
|
<view class="page"> |
||||
|
<view class="fixed-head"> |
||||
|
<view class="nav" :style="{'height': navHeight + 'px'}"> |
||||
|
<view class="back-btn" :style="{'top': backTop + 'px'}" @tap="back"> |
||||
|
<uni-icons type="left" size="26"></uni-icons> |
||||
|
</view> |
||||
|
<view class="nav-title">自配送订单</view> |
||||
|
</view> |
||||
|
<view class="tabs"> |
||||
|
<view class="tab" :class="{active: tab === 'pickup'}" @tap="switchTab('pickup')">待取货</view> |
||||
|
<view class="tab" :class="{active: tab === 'complete'}" @tap="switchTab('complete')">待送达</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<scroll-view class="list" :style="{'top': listTop + 'px'}" scroll-y @scrolltolower="loadMore"> |
||||
|
<view class="card" v-for="(item,index) in list" :key="item.id"> |
||||
|
<view class="card-head"> |
||||
|
<view class="tag">商家自配送</view> |
||||
|
<view class="time">{{item.mustFinishTime | formatTime}}前送达</view> |
||||
|
</view> |
||||
|
<view class="order-code" v-if="item.numberCode">#{{item.numberCode}}</view> |
||||
|
<view class="address"> |
||||
|
<view class="dot get">取</view> |
||||
|
<view> |
||||
|
<view class="name">{{item.shopName}}</view> |
||||
|
<view class="desc">{{item.shopAddress}}</view> |
||||
|
<view class="phone-row" v-if="item.shopPhone" @tap.stop="makeCall(item.shopPhone)"> |
||||
|
商家电话:{{item.shopPhone}} |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="address"> |
||||
|
<view class="dot send">送</view> |
||||
|
<view> |
||||
|
<view class="name">{{item.receiverName}} {{item.receiverPhone}}</view> |
||||
|
<view class="desc">{{item.receiverAddress}}</view> |
||||
|
<view class="phone-row" v-if="item.receiverPhone" @tap.stop="makeCall(item.receiverPhone)"> |
||||
|
用户电话:{{item.receiverPhone}} |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="goods-row" v-if="item.goodsList && item.goodsList.length > 0" @tap="productDetail(item)"> |
||||
|
<view class="goods-title">商品详情</view> |
||||
|
<view class="goods-desc">共{{goodsCount(item)}}件,点击查看规格和数量</view> |
||||
|
<uni-icons type="right" size="14" color="#7a8582"></uni-icons> |
||||
|
</view> |
||||
|
<view class="remark" v-if="item.remark">备注:{{item.remark}}</view> |
||||
|
<view class="fee">配送费 ¥{{item.deliveryFee || 0}}</view> |
||||
|
<view class="btn" @tap="handleOrder(index,item)"> |
||||
|
{{buttonText(item)}} |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="empty" v-if="list.length === 0 && loadStatus === 'nomore'">暂无订单</view> |
||||
|
</scroll-view> |
||||
|
<uni-popup ref="productPopup" background-color="transparent"> |
||||
|
<view class="product-popup-content"> |
||||
|
<view class="product-popup-header"> |
||||
|
<view class="product-popup-title">商品详情</view> |
||||
|
<view class="product-popup-subtitle">请核对商品规格和数量</view> |
||||
|
</view> |
||||
|
<view class="product-popup-list" v-if="productData && productData.length > 0"> |
||||
|
<view class="product-popup-card" v-for="(item1,index1) in productData" :key="index1"> |
||||
|
<view class="product-popup-img"> |
||||
|
<img :src="item1.productPicture" alt=""> |
||||
|
</view> |
||||
|
<view class="product-popup-info"> |
||||
|
<view class="product-popup-name">{{item1.productName}}</view> |
||||
|
<view class="product-popup-spec"> |
||||
|
<text class="product-popup-label">规格</text> |
||||
|
<text class="product-popup-spec-text">{{formatSpecs(item1.specs)}}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="product-popup-quantity"> |
||||
|
<view class="product-popup-quantity-num">x{{item1.quantity}}</view> |
||||
|
<view class="product-popup-quantity-label">数量</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="product-popup-empty" v-else>暂无商品详情</view> |
||||
|
</view> |
||||
|
</uni-popup> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
tab: 'pickup', |
||||
|
list: [], |
||||
|
loadStatus: 'more', |
||||
|
totalPages: 1, |
||||
|
searchForm: { |
||||
|
pageNum: 1, |
||||
|
pageSize: 10, |
||||
|
status: 1, |
||||
|
deliveryType: 1, |
||||
|
shopDelivery: 1, |
||||
|
shopId: '', |
||||
|
workerId: '', |
||||
|
regionId: '' |
||||
|
}, |
||||
|
menuButtonInfo: {}, |
||||
|
navHeight: 120, |
||||
|
backTop: 88, |
||||
|
listTop: 210, |
||||
|
productData: [] |
||||
|
} |
||||
|
}, |
||||
|
filters: { |
||||
|
formatTime(value) { |
||||
|
if (!value) return ''; |
||||
|
const date = new Date(value); |
||||
|
return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`; |
||||
|
} |
||||
|
}, |
||||
|
onShow() { |
||||
|
this.initNavMetrics(); |
||||
|
this.initWorkerFromShop(); |
||||
|
this.resetList(); |
||||
|
}, |
||||
|
methods: { |
||||
|
initNavMetrics() { |
||||
|
const rect = uni.getMenuButtonBoundingClientRect(); |
||||
|
this.menuButtonInfo = rect || {}; |
||||
|
const top = rect && rect.top ? rect.top : 32; |
||||
|
const height = rect && rect.height ? rect.height : 32; |
||||
|
this.backTop = top + 6; |
||||
|
this.navHeight = top + height + 52; |
||||
|
this.listTop = this.navHeight + 76; |
||||
|
}, |
||||
|
back() { |
||||
|
uni.navigateBack(); |
||||
|
}, |
||||
|
initWorkerFromShop() { |
||||
|
const shop = this.getCurrentShop(); |
||||
|
this.searchForm.shopId = uni.getStorageSync('shopId'); |
||||
|
this.searchForm.regionId = JSON.parse(uni.getStorageSync('area')).id; |
||||
|
this.searchForm.workerId = shop.workerId || ''; |
||||
|
this.worker = { |
||||
|
workerId: shop.workerId || '', |
||||
|
workerName: shop.workerName || '', |
||||
|
workerPhone: shop.workerPhone || '' |
||||
|
}; |
||||
|
}, |
||||
|
getCurrentShop() { |
||||
|
const shopList = uni.getStorageSync('schoolShop') || []; |
||||
|
const shopId = uni.getStorageSync('shopId'); |
||||
|
if (Array.isArray(shopList)) { |
||||
|
return shopList.find(item => item.id == shopId) || {}; |
||||
|
} |
||||
|
return {}; |
||||
|
}, |
||||
|
switchTab(tab) { |
||||
|
this.tab = tab; |
||||
|
this.resetList(); |
||||
|
}, |
||||
|
resetList() { |
||||
|
this.list = []; |
||||
|
this.searchForm.pageNum = 1; |
||||
|
this.totalPages = 1; |
||||
|
this.loadStatus = 'more'; |
||||
|
this.searchForm.status = this.tab === 'pickup' ? 1 : 2; |
||||
|
this.getList(); |
||||
|
}, |
||||
|
loadMore() { |
||||
|
if (this.searchForm.pageNum >= this.totalPages) return; |
||||
|
this.searchForm.pageNum++; |
||||
|
this.getList(); |
||||
|
}, |
||||
|
getList() { |
||||
|
if (!this.searchForm.workerId) { |
||||
|
this.loadStatus = 'nomore'; |
||||
|
this.tui.toast('店铺未绑定自配送配送员'); |
||||
|
return; |
||||
|
} |
||||
|
this.tui.request('/mall/delivery/pageShopDelivery', 'POST', this.searchForm, false, false).then((res) => { |
||||
|
if (res.code == 200) { |
||||
|
const records = res.result.records || []; |
||||
|
this.list = this.searchForm.pageNum == 1 ? records : this.list.concat(records); |
||||
|
this.totalPages = res.result.pages || 1; |
||||
|
this.loadStatus = this.searchForm.pageNum >= this.totalPages ? 'nomore' : 'more'; |
||||
|
} else { |
||||
|
this.tui.toast(res.message); |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
handleOrder(index, item) { |
||||
|
if (this.tab === 'pickup' && !item.arriveTime) { |
||||
|
this.changeStatus(index, item, 1); |
||||
|
return; |
||||
|
} |
||||
|
this.changeStatus(index, item, this.tab === 'pickup' ? 2 : 3); |
||||
|
}, |
||||
|
buttonText(item) { |
||||
|
if (this.tab === 'pickup') { |
||||
|
return item.arriveTime ? '确认取货' : '已到店'; |
||||
|
} |
||||
|
return '确认送达'; |
||||
|
}, |
||||
|
changeStatus(index, item, status) { |
||||
|
const url = status === 1 ? '/mall/delivery/arriveShop' : (status === 2 ? '/mall/delivery/pickup' : '/mall/delivery/complete'); |
||||
|
this.tui.request(url, 'POST', { |
||||
|
regionId: this.searchForm.regionId, |
||||
|
deliveryId: item.id, |
||||
|
workerId: this.worker.workerId |
||||
|
}, false, true).then((res) => { |
||||
|
if (res.code == 200) { |
||||
|
if (status === 1) { |
||||
|
this.$set(this.list[index], 'arriveTime', new Date()); |
||||
|
} else { |
||||
|
this.list.splice(index, 1); |
||||
|
} |
||||
|
this.tui.toast(res.message || '操作成功'); |
||||
|
} else { |
||||
|
this.tui.toast(res.message); |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
productDetail(item) { |
||||
|
this.productData = item.goodsList || []; |
||||
|
this.$refs.productPopup.open(); |
||||
|
}, |
||||
|
goodsCount(item) { |
||||
|
return (item.goodsList || []).reduce((sum, goods) => sum + Number(goods.quantity || 0), 0); |
||||
|
}, |
||||
|
formatSpecs(data) { |
||||
|
let str; |
||||
|
if (typeof data === 'object' && data !== null) { |
||||
|
str = JSON.stringify(data); |
||||
|
} else if (typeof data === 'string') { |
||||
|
str = data; |
||||
|
} else { |
||||
|
str = String(data || ''); |
||||
|
} |
||||
|
return str.replace(/[{}"]/g, ''); |
||||
|
}, |
||||
|
makeCall(phone) { |
||||
|
if (!phone) return; |
||||
|
uni.makePhoneCall({ |
||||
|
phoneNumber: phone |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.page{ |
||||
|
height: 100vh; |
||||
|
background: #F5F8F5; |
||||
|
padding: 0 24rpx; |
||||
|
box-sizing: border-box; |
||||
|
position: relative; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
.fixed-head{ |
||||
|
position: absolute; |
||||
|
top: -40rpx; |
||||
|
left: 24rpx; |
||||
|
right: 24rpx; |
||||
|
z-index: 10; |
||||
|
background: #F5F8F5; |
||||
|
} |
||||
|
.nav{ |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
.back-btn{ |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
width: 68rpx; |
||||
|
height: 68rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
border-radius: 50%; |
||||
|
background: rgba(255, 255, 255, 0.9); |
||||
|
box-shadow: 0 8rpx 20rpx rgba(0, 35, 28, 0.08); |
||||
|
} |
||||
|
.nav-title{ |
||||
|
position: absolute; |
||||
|
bottom: 18rpx; |
||||
|
font-size: 32rpx; |
||||
|
font-weight: 800; |
||||
|
} |
||||
|
.tabs{ |
||||
|
display: flex; |
||||
|
background: #fff; |
||||
|
border-radius: 24rpx; |
||||
|
padding: 10rpx; |
||||
|
margin-bottom: 0; |
||||
|
} |
||||
|
.tab{ |
||||
|
flex: 1; |
||||
|
text-align: center; |
||||
|
height: 70rpx; |
||||
|
line-height: 70rpx; |
||||
|
border-radius: 20rpx; |
||||
|
font-weight: 700; |
||||
|
} |
||||
|
.tab.active{ |
||||
|
background: linear-gradient(90deg, #e3ff96, #a6ffea); |
||||
|
} |
||||
|
.list{ |
||||
|
position: absolute; |
||||
|
left: 24rpx; |
||||
|
right: 24rpx; |
||||
|
bottom: 0; |
||||
|
box-sizing: border-box; |
||||
|
padding-top: 20rpx; |
||||
|
padding-bottom: 36rpx; |
||||
|
} |
||||
|
.card{ |
||||
|
background: #fff; |
||||
|
border-radius: 24rpx; |
||||
|
padding: 24rpx; |
||||
|
margin-bottom: 20rpx; |
||||
|
box-shadow: 0 12rpx 30rpx rgba(0, 35, 28, 0.08); |
||||
|
} |
||||
|
.card-head{ |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
} |
||||
|
.tag{ |
||||
|
background: rgba(255, 111, 44, 0.1); |
||||
|
color: #ff6f2c; |
||||
|
padding: 8rpx 16rpx; |
||||
|
border-radius: 18rpx; |
||||
|
font-weight: 700; |
||||
|
} |
||||
|
.time{ |
||||
|
color: #666; |
||||
|
} |
||||
|
.order-code{ |
||||
|
margin-top: 18rpx; |
||||
|
font-size: 32rpx; |
||||
|
font-weight: 800; |
||||
|
} |
||||
|
.address{ |
||||
|
display: flex; |
||||
|
margin-top: 18rpx; |
||||
|
} |
||||
|
.dot{ |
||||
|
width: 44rpx; |
||||
|
height: 44rpx; |
||||
|
line-height: 44rpx; |
||||
|
text-align: center; |
||||
|
border-radius: 50%; |
||||
|
color: #fff; |
||||
|
margin-right: 16rpx; |
||||
|
} |
||||
|
.get{ |
||||
|
background: #0b9b73; |
||||
|
} |
||||
|
.send{ |
||||
|
background: #ff6f2c; |
||||
|
} |
||||
|
.name{ |
||||
|
font-weight: 700; |
||||
|
} |
||||
|
.desc,.remark,.fee{ |
||||
|
margin-top: 8rpx; |
||||
|
color: #666; |
||||
|
line-height: 38rpx; |
||||
|
} |
||||
|
.phone-row{ |
||||
|
display: inline-block; |
||||
|
margin-top: 8rpx; |
||||
|
color: #0b9b73; |
||||
|
font-weight: 700; |
||||
|
line-height: 34rpx; |
||||
|
} |
||||
|
.goods-row{ |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin-top: 20rpx; |
||||
|
padding: 18rpx 20rpx; |
||||
|
border-radius: 18rpx; |
||||
|
background: rgba(247, 248, 248, 0.8); |
||||
|
} |
||||
|
.goods-title{ |
||||
|
font-weight: 800; |
||||
|
margin-right: 16rpx; |
||||
|
} |
||||
|
.goods-desc{ |
||||
|
flex: 1; |
||||
|
color: #666; |
||||
|
font-size: 24rpx; |
||||
|
} |
||||
|
.btn{ |
||||
|
margin-top: 24rpx; |
||||
|
height: 76rpx; |
||||
|
line-height: 76rpx; |
||||
|
text-align: center; |
||||
|
border-radius: 38rpx; |
||||
|
background: linear-gradient(90deg, #e3ff96, #a6ffea); |
||||
|
font-weight: 800; |
||||
|
} |
||||
|
.empty{ |
||||
|
text-align: center; |
||||
|
color: #888; |
||||
|
padding: 80rpx 0; |
||||
|
} |
||||
|
.product-popup-content{ |
||||
|
width: 650rpx; |
||||
|
max-height: 850rpx; |
||||
|
padding: 30rpx; |
||||
|
border-radius: 28rpx; |
||||
|
background: #fff; |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
.product-popup-title{ |
||||
|
font-size: 32rpx; |
||||
|
font-weight: 800; |
||||
|
} |
||||
|
.product-popup-subtitle{ |
||||
|
margin-top: 8rpx; |
||||
|
color: #888; |
||||
|
font-size: 24rpx; |
||||
|
} |
||||
|
.product-popup-list{ |
||||
|
margin-top: 24rpx; |
||||
|
max-height: 680rpx; |
||||
|
overflow-y: auto; |
||||
|
} |
||||
|
.product-popup-card{ |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
padding: 18rpx 0; |
||||
|
border-bottom: 1rpx solid #f0f0f0; |
||||
|
} |
||||
|
.product-popup-img img{ |
||||
|
width: 90rpx; |
||||
|
height: 90rpx; |
||||
|
border-radius: 16rpx; |
||||
|
margin-right: 18rpx; |
||||
|
} |
||||
|
.product-popup-info{ |
||||
|
flex: 1; |
||||
|
min-width: 0; |
||||
|
} |
||||
|
.product-popup-name{ |
||||
|
font-weight: 800; |
||||
|
line-height: 38rpx; |
||||
|
} |
||||
|
.product-popup-spec{ |
||||
|
margin-top: 8rpx; |
||||
|
color: #777; |
||||
|
font-size: 24rpx; |
||||
|
} |
||||
|
.product-popup-label{ |
||||
|
margin-right: 10rpx; |
||||
|
color: #999; |
||||
|
} |
||||
|
.product-popup-quantity{ |
||||
|
width: 80rpx; |
||||
|
text-align: right; |
||||
|
} |
||||
|
.product-popup-quantity-num{ |
||||
|
font-weight: 800; |
||||
|
} |
||||
|
.product-popup-quantity-label, |
||||
|
.product-popup-empty{ |
||||
|
color: #888; |
||||
|
font-size: 22rpx; |
||||
|
} |
||||
|
</style> |
||||
Loading…
Reference in new issue