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.
 
 
 
 
 

617 lines
14 KiB

<template>
<view class="page">
<view class="fixed-head">
<view class="nav" :style="{'height': navHeight + 'px'}">
<view class="back" :style="{'top': backTop + 'px'}" @tap="back"><uni-icons type="left"
size="26"></uni-icons></view>
<view class="title"
:style="{'top': titleTop + 'px','height': capsuleHeight + 'px','line-height': capsuleHeight + 'px'}">
中转配送</view>
</view>
<view class="tabs">
<view class="tab" :class="{active: tab === 'wait'}" @tap="switchTab('wait')">待送达</view>
<view class="tab" :class="{active: tab === 'done'}" @tap="switchTab('done')">已送达</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" @tap="productDetail(item)">
<view class="card-head">
<view class="tag">中转配送</view>
<view class="appointment-delivery-tag" v-if="item.appointmentDelivery == 1">预约配送</view>
<view class="code" v-if="item.numberCode">#{{item.numberCode}}</view>
</view>
<view class="row">
<view class="label">中转点</view>
<view class="value">{{item.transferAddressName || item.shopName}}</view>
</view>
<view class="row">
<view class="label">下单时间</view>
<view class="value">{{formatTime(item.createTime)}}</view>
</view>
<view class="row" v-if="item.mustFinishTime">
<view class="label">要求送达</view>
<view class="value">{{formatTime(item.mustFinishTime)}}</view>
</view>
<view class="row" v-if="getTransferArriveText(item)">
<view class="label">中转送达</view>
<view class="value">{{getTransferArriveText(item)}}前需要送达中转点</view>
</view>
<view class="row">
<view class="label">取餐码</view>
<view class="value pickup-code">{{item.transferPickupCode || '配送员接单后生成'}}</view>
</view>
<view class="row">
<view class="label">配送员</view>
<view class="value phone-value" @tap.stop="makeCall(item.workerPhone)">
<text>{{item.workerName || '未分配'}}</text>
<text v-if="item.workerPhone" class="phone">{{item.workerPhone}}</text>
</view>
</view>
<view class="row">
<view class="label">用户</view>
<view class="value phone-value" @tap.stop="makeCall(item.receiverPhone)">
<text>{{item.receiverName}}</text>
<text v-if="item.receiverPhone" class="phone">{{item.receiverPhone}}</text>
</view>
</view>
<view class="address">{{item.receiverAddress}}</view>
<view class="image-row" v-if="item.transferArriveImage">
<image :src="item.transferArriveImage" mode="aspectFill"></image>
</view>
<view class="arrive-info" v-if="item.transferArriveTime">
已送达中转点:{{formatTime(item.transferArriveTime)}}
</view>
<view class="actions" v-else>
<view class="photo-btn" @tap.stop="chooseImage(index)">拍照</view>
<view class="main-btn" @tap.stop="transferArrive(index,item)">送达中转点</view>
</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 != null && productData.length > 0">
<view class="product-popup-card" v-for="(item1,index1) in productData" :key="index1">
<view class="product-popup-img">
<image :src="item1.productPicture" mode="aspectFill"></image>
</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">{{item1.specs | delNode}}</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: 'wait',
list: [],
productData: [],
loadStatus: 'more',
totalPages: 1,
searchForm: {
pageNum: 1,
pageSize: 10,
deliveryType: 1,
transferDelivery: 1,
transferArriveEmpty: true,
shopId: '',
regionId: ''
},
navHeight: 120,
backTop: 50,
titleTop: 50,
capsuleHeight: 32,
listTop: 210
}
},
onShow() {
this.initNavMetrics()
this.resetList()
},
filters: {
delNode(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, '')
}
},
methods: {
initNavMetrics() {
const top = 32
const height = 32
this.capsuleHeight = height
this.navHeight = top + height + 30
this.listTop = this.navHeight + 48
},
back() {
uni.navigateBack()
},
switchTab(tab) {
if (this.tab === tab) return
this.tab = tab
this.resetList()
},
resetList() {
this.searchForm.pageNum = 1
this.totalPages = 1
this.list = []
this.loadStatus = 'more'
this.searchForm.transferArriveEmpty = this.tab === 'wait'
this.searchForm.shopId = uni.getStorageSync('shopId')
const area = uni.getStorageSync('area')
this.searchForm.regionId = area ? JSON.parse(area).regionId : ''
this.getList()
},
loadMore() {
if (this.searchForm.pageNum >= this.totalPages) return
this.searchForm.pageNum++
this.getList()
},
getList() {
this.NB.sendRequest("/mall/delivery/pageTransferDelivery", this.searchForm, false, 'post',
'application/json').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 {
uni.showToast({
title: res.message,
icon: 'none'
})
}
})
},
chooseImage(index) {
uni.chooseMedia({
count: 1,
mediaType: ['image'],
sourceType: ['camera', 'album'],
success: (res) => {
const file = res.tempFiles && res.tempFiles[0] ? res.tempFiles[0].tempFilePath : ''
if (file) this.uploadImage(index, file)
}
})
},
uploadImage(index, filePath) {
uni.uploadFile({
url: getApp().globalData.mainurl + '/upload/file',
filePath,
name: 'file',
header: {
'accessToken': uni.getStorageSync('hiver_token'),
'content-type': 'multipart/form-data'
},
success: (res) => {
const data = JSON.parse(res.data)
this.$set(this.list[index], 'transferArriveImage', data.result)
}
})
},
transferArrive(index, item) {
this.NB.sendRequest("/mall/delivery/transferArrive", {
deliveryId: item.id,
transferArriveImage: item.transferArriveImage || ''
}, false, 'post', 'application/x-www-form-urlencoded').then((res) => {
if (res.code == 200) {
uni.showToast({
title: '操作成功',
icon: 'none'
})
this.resetList()
} else {
uni.showToast({
title: res.message,
icon: 'none'
})
}
})
},
productDetail(item) {
const goodsList = item.goodsList || item.mallOrderGoodsList || item.orderGoodsList || []
this.productData = goodsList
this.$refs.productPopup.open()
},
getTransferArriveText(item) {
const address = item && item.shopAddress ? String(item.shopAddress) : ''
const match = address.match(/商家预计(.+?)配送到-/)
return match && match[1] ? match[1] : ''
},
formatTime(value) {
if (!value) return '';
const date = new Date(value)
if (isNaN(date.getTime())) return ''
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hour = String(date.getHours()).padStart(2, '0')
const minute = String(date.getMinutes()).padStart(2, '0')
const second = String(date.getSeconds()).padStart(2, '0')
return `${year}-${month}-${day} ${hour}:${minute}:${second}`
},
makeCall(phone) {
if (!phone) {
uni.showToast({
title: '暂无手机号',
icon: 'none'
})
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: 0;
left: 24rpx;
right: 24rpx;
z-index: 9;
background: #F5F8F5;
}
.nav {
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.back {
position: absolute;
left: 0;
width: 68rpx;
height: 68rpx;
border-radius: 50%;
background: #fff;
display: flex;
align-items: center;
justify-content: center;
}
.title {
position: absolute;
font-size: 32rpx;
font-weight: 800;
text-align: center;
}
.tabs {
display: flex;
background: #fff;
border-radius: 24rpx;
padding: 10rpx;
}
.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 {
width: 94%;
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 10rpx 26rpx rgba(0, 35, 28, .08);
}
.card-head {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.tag {
background: #fff2e9;
color: #f36c21;
border-radius: 12rpx;
padding: 8rpx 14rpx;
font-weight: 700;
}
.appointment-delivery-tag {
margin-left: 12rpx;
padding: 8rpx 14rpx;
border-radius: 12rpx;
background: linear-gradient(135deg, #ff8f1f 0%, #ff6a3d 100%);
color: #fff;
font-size: 22rpx;
font-weight: 800;
box-shadow: 0 8rpx 18rpx rgba(255, 106, 61, .16);
}
.code {
margin-left: auto;
font-weight: 800;
font-size: 32rpx;
}
.row {
display: flex;
line-height: 46rpx;
}
.label {
width: 120rpx;
color: #7a8582;
}
.value {
flex: 1;
font-weight: 700;
}
.phone-value {
display: flex;
align-items: center;
gap: 16rpx;
color: #123d35;
}
.phone {
color: #0b9b73;
text-decoration: underline;
}
.pickup-code {
color: #f36c21;
font-size: 34rpx;
letter-spacing: 4rpx;
}
.address {
margin-top: 10rpx;
color: #6c7773;
line-height: 40rpx;
}
.image-row image {
width: 160rpx;
height: 160rpx;
border-radius: 16rpx;
margin-top: 16rpx;
}
.arrive-info {
margin-top: 22rpx;
padding: 18rpx 20rpx;
border-radius: 18rpx;
background: #f2f6f4;
color: #0b9b73;
font-weight: 700;
text-align: center;
}
.actions {
display: flex;
margin-top: 22rpx;
gap: 18rpx;
}
.photo-btn,
.main-btn {
flex: 1;
height: 76rpx;
line-height: 76rpx;
text-align: center;
border-radius: 38rpx;
font-weight: 700;
}
.photo-btn {
background: #f2f6f4;
color: #0b9b73;
}
.main-btn {
background: linear-gradient(90deg, #e3ff96, #a6ffea);
color: #00231c;
}
.empty {
text-align: center;
color: #8a9692;
padding: 80rpx 0;
}
.product-popup-content {
width: 680rpx;
max-height: 880rpx;
padding: 34rpx 28rpx 30rpx;
box-sizing: border-box;
overflow: scroll;
border-radius: 36rpx;
background:
radial-gradient(circle at 92% 4%, rgba(166, 255, 234, 0.38) 0, rgba(166, 255, 234, 0) 180rpx),
linear-gradient(180deg, #ffffff 0%, #f8fffc 100%);
box-shadow: 0 24rpx 56rpx rgba(0, 35, 28, 0.18);
}
.product-popup-header {
margin-bottom: 26rpx;
text-align: center;
}
.product-popup-title {
color: #143d35;
font-size: 36rpx;
font-weight: 900;
line-height: 48rpx;
}
.product-popup-subtitle {
margin-top: 8rpx;
color: #7b8a85;
font-size: 24rpx;
line-height: 34rpx;
}
.product-popup-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.product-popup-card {
display: flex;
align-items: center;
padding: 22rpx;
border: 1px solid rgba(166, 255, 234, 0.58);
border-radius: 28rpx;
background: rgba(255, 255, 255, 0.88);
box-shadow: 0 12rpx 28rpx rgba(0, 35, 28, 0.06);
}
.product-popup-img {
width: 128rpx;
height: 128rpx;
flex-shrink: 0;
overflow: hidden;
border-radius: 22rpx;
background: #f0f5f3;
}
.product-popup-img image {
width: 100%;
height: 100%;
}
.product-popup-info {
flex: 1;
min-width: 0;
padding: 0 22rpx;
}
.product-popup-name {
color: #172f2a;
font-size: 30rpx;
font-weight: 900;
line-height: 42rpx;
}
.product-popup-spec {
display: flex;
align-items: flex-start;
margin-top: 16rpx;
}
.product-popup-label {
flex-shrink: 0;
height: 34rpx;
padding: 0 14rpx;
border-radius: 999rpx;
background: rgba(166, 255, 234, 0.72);
color: #126255;
font-size: 20rpx;
font-weight: 800;
line-height: 34rpx;
}
.product-popup-spec-text {
flex: 1;
margin-left: 12rpx;
color: #65736f;
font-size: 24rpx;
line-height: 34rpx;
word-break: break-all;
}
.product-popup-quantity {
width: 92rpx;
height: 92rpx;
flex-shrink: 0;
border-radius: 28rpx;
background: linear-gradient(135deg, #fff4ce 0%, #a6ffea 100%);
color: #103f36;
text-align: center;
box-shadow: 0 10rpx 20rpx rgba(0, 35, 28, 0.08);
}
.product-popup-quantity-num {
padding-top: 16rpx;
font-size: 30rpx;
font-weight: 900;
line-height: 34rpx;
}
.product-popup-quantity-label {
margin-top: 2rpx;
color: #52736b;
font-size: 20rpx;
line-height: 28rpx;
}
.product-popup-empty {
padding: 80rpx 20rpx;
border-radius: 28rpx;
background: rgba(247, 255, 251, 0.9);
color: #7b8a85;
font-size: 28rpx;
font-weight: 700;
text-align: center;
}
</style>