Browse Source

‘新增物流信息’

master
Lj 1 week ago
parent
commit
5c057ab841
7 changed files with 362 additions and 20 deletions
  1. +5
    -1
      api/model/index.js
  2. +1
    -1
      config.js
  3. +7
    -0
      pages.json
  4. +18
    -3
      pages/manager/order-detail.vue
  5. +30
    -8
      pages/subcomponent/detail.vue
  6. +13
    -7
      pages/subcomponent/inspection-report.vue
  7. +288
    -0
      pages/subcomponent/logistics.vue

+ 5
- 1
api/model/index.js View File

@ -253,7 +253,11 @@ const api = {
method: 'GET',
auth: true,
},
queryTrace: {
url: '/recycle-admin/applet/order/queryTrace',
method: 'GET',
auth: true,
},
}

+ 1
- 1
config.js View File

@ -1,5 +1,5 @@
// config.js
const type = 'dev'
const type = 'prod'
const config = {
local: {


+ 7
- 0
pages.json View File

@ -253,6 +253,13 @@
"navigationStyle": "custom",
"enablePullDownRefresh": true
}
},
{
"path": "logistics",
"style": {
"navigationStyle": "custom",
"enablePullDownRefresh": true
}
}
]
},


+ 18
- 3
pages/manager/order-detail.vue View File

@ -39,7 +39,10 @@
</view>
<view class="base-value-wrap">
<text class="base-value">{{ item.value }}</text>
<text v-if="item.copy" class="copy-btn" @tap="copyText(item.value)">复制</text>
<view class="base-actions">
<text v-if="item.viewLogistics" class="view-logistics-btn" @tap="viewLogistics(item.value, item.expressCompany)">查看物流</text>
<text v-if="item.copy" class="copy-btn" @tap="copyText(item.value)">复制</text>
</view>
<uni-icons v-if="item.arrow" type="right" size="18" color="#bbb" />
</view>
<view v-if="i < baseInfo.length-1" class="divider"></view>
@ -231,7 +234,7 @@ export default {
//
this.baseInfo = [
{ label: '订单编号', value: data.ordeNo || data.id, copy: true },
...(data.wliuNo ? [{ label: '快递单号', value: data.wliuNo, copy: true }] : []),
...(data.wliuNo && data.wliu ? [{ label: '快递单号', value: data.wliuNo, copy: true, viewLogistics: true, expressCompany: data.wliu }] : []),
{ label: '用户名', value: data.name, arrow: true },
{ label: '取件地址', value: (data.address || '') + (data.addressDetail || '') },
{ label: '预约时间', value: data.goTime || data.createTime }
@ -265,6 +268,15 @@ export default {
copyText(text) {
uni.setClipboardData({ data: text })
},
viewLogistics(expressNumber, expressCompany) {
if (!expressNumber) {
uni.showToast({ title: '暂无快递单号', icon: 'none' });
return;
}
uni.navigateTo({
url: `/pages/subcomponent/logistics?wliuNo=${expressNumber}&expressCompany=${expressCompany || ''}`
});
},
maskPhone(phone) {
if (!phone) return '';
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
@ -594,7 +606,10 @@ $order-card-padding: 40px 28px;
.base-label-wrap { min-width: 80px; text-align: left; color: #8b8b8b; font-size: 13px;}
.base-value-wrap { display: flex; align-items: center; flex: 1; justify-content: flex-end; }
.base-value { color: #222; font-size: 15px; font-weight: 600; word-break: break-all; text-align: right; }
.copy-btn { color: #ffb400; font-size: 13px; margin-left: 6px; font-weight: 400; padding: 1px 6px; border-radius: 6px; transition: background 0.2s; }
.base-actions { display: flex; align-items: center; gap: 8px; }
.view-logistics-btn { color: #2a9cfb; font-size: 13px; font-weight: 400; padding: 1px 6px; border-radius: 6px; transition: background 0.2s; }
.view-logistics-btn:active { background: #eaf6ff; }
.copy-btn { color: #ffb400; font-size: 13px; font-weight: 400; padding: 1px 6px; border-radius: 6px; transition: background 0.2s; }
.copy-btn:active { background: #fff7e6; }
.divider { position: absolute; left: 16px; right: 16px; bottom: -1px; height: 1px; background: #f0f0f0; }
uni-icons { margin-left: 6px; }


+ 30
- 8
pages/subcomponent/detail.vue View File

@ -49,12 +49,15 @@
<!-- 取消后提示语 -->
<view v-if="state === 3" class="cancel-tip">期待您下次的支持共同为地球减少碳排放出一份力</view>
<!-- 物流公司仅status为01时显示 -->
<view class="express-info" v-if="state !== 3 && status <= 1">
<!-- 物流公司仅status为01时显示且必须有物流信息 -->
<view class="express-info" v-if="state !== 3||4 && status <= 1 && expressCompany && wuliuNo">
<text class="express-label">物流公司</text>
<view class="express-row">
<text class="express-value">{{ expressCompany }}{{ expressNo }}</text>
<text class="express-copy" @tap="copyExpressNo">复制</text>
<text class="express-value">{{ expressCompany }}{{ wuliuNo }}</text>
<view class="express-actions">
<text class="express-view" @tap="viewLogistics">查看物流</text>
<text class="express-copy" @tap="copyExpressNo">复制</text>
</view>
</view>
</view>
<view class="info-divider" v-if="state !== 3 && status < 3"></view>
@ -198,7 +201,7 @@ export default {
reportTime: '',
showEditButton: true,
expressCompany: '',
expressNo: '',
wuliuNo: '',
showCancelModal: false,
statusBarHeight: 0,
orderDetail: null,
@ -313,12 +316,21 @@ export default {
},
copyExpressNo() {
uni.setClipboardData({
data: this.expressCompany + this.expressNo,
data: this.expressCompany + this.wuliuNo,
success: () => {
uni.showToast({ title: '已复制', icon: 'none' });
}
});
},
viewLogistics() {
if (!this.wuliuNo) {
uni.showToast({ title: '暂无快递单号', icon: 'none' });
return;
}
uni.navigateTo({
url: `/pages/subcomponent/logistics?wliuNo=${this.wuliuNo}&expressCompany=${this.expressCompany}`
});
},
confirmCancelOrder() {
this.showCancelModal = false;
//
@ -401,7 +413,7 @@ export default {
this.finalPrice = res.result.price || ''
this.clothesList = res.result.commonOrderList || []
this.expressCompany = res.result.wliu || ''
this.expressNo = res.result.expressNo || ''
this.wuliuNo = res.result.wliuNo || ''
this.phone = res.result.deliveryPhone || ''
//
this.setOrderStatus(res.result.status, res.result.state, res.result)
@ -887,12 +899,22 @@ letter-spacing: 0%;
font-size: 28rpx;
font-weight: 500;
word-break: break-all;
flex: 1;
}
.express-actions {
display: flex;
align-items: center;
gap: 16rpx;
}
.express-view {
color: #2a9cfb;
font-size: 26rpx;
font-weight: 500;
}
.express-copy {
color: #ff9c00;
font-size: 26rpx;
font-weight: 500;
margin-left: 16rpx;
}
.modal-mask {
position: fixed;


+ 13
- 7
pages/subcomponent/inspection-report.vue View File

@ -52,7 +52,7 @@
<text class="goods-unit">/{{item.unit}}</text>
<text class="goods-count">x{{item.count}}</text>
</view>
<view class="goods-total">{{item.total}}</view>
<!-- <view class="goods-total">{{item.total}}</view> -->
</view>
</view>
</view>
@ -94,7 +94,7 @@
</view>
<view class="summary-row">
<text>件数<text class="highlight">{{problemCount}}</text> </text>
<text>结算金额<text class="highlight"> 0</text></text>
<text>结算金额<text class="highlight">{{problemAmount}}</text></text>
</view>
</view>
<view v-if="showUnrecyclable && unrecyclableList.length" class="inspection-card problem-card">
@ -124,8 +124,8 @@
<uni-icons :type="unrecyclableCollapsed ? 'arrowdown' : 'arrowup'" size="16" color="#bbb" />
</view>
<view class="summary-row">
<text>件数<text class="highlight">{{problemCount}}</text> </text>
<text>结算金额<text class="highlight"> 0</text></text>
<text>件数<text class="highlight">{{unrecyclableCount}}</text> </text>
<text>结算金额<text class="highlight">{{unrecyclableAmount}}</text></text>
</view>
</view>
</view>
@ -174,7 +174,13 @@ export default {
return this.problemList.reduce((sum, item) => sum + item.count, 0)
},
problemAmount() {
return this.problemList.reduce((sum, item) => sum + item.total, 0).toFixed(1)
return this.problemList.reduce((sum, item) => sum + item.total, 0).toFixed(2)
},
unrecyclableCount() {
return this.unrecyclableList.reduce((sum, item) => sum + item.count, 0)
},
unrecyclableAmount() {
return this.unrecyclableList.reduce((sum, item) => sum + item.total, 0).toFixed(2)
},
fixedHeaderHeight() {
// + + (140rpxpx)
@ -249,7 +255,7 @@ export default {
desc: item.pinName ? `${item.pinName}` : '',
price: item.price || 0,
count: sub.num || 1,
total: 0,
total: (item.price || 0) * (sub.num || 1),
detail: true,
testingInstructions: sub.testingInstructions || '',
testingImages: sub.testingImages || '',
@ -265,7 +271,7 @@ export default {
desc: item.pinName ? `${item.pinName}` : '',
price: item.price || 0,
count: sub.num || 1,
total: 0,
total: (item.price || 0) * (sub.num || 1),
detail: true,
testingInstructions: sub.testingInstructions || '',
testingImages: sub.testingImages || '',


+ 288
- 0
pages/subcomponent/logistics.vue View File

@ -0,0 +1,288 @@
<template>
<view class="logistics-page">
<!-- 顶部导航 -->
<view class="nav-bar">
<view class="back" @tap="goBack">
<uni-icons type="left" size="20" color="#222" />
</view>
<text class="nav-title">物流轨迹</text>
</view>
<!-- 物流信息卡片 -->
<view class="logistics-card">
<view class="express-info">
<view class="express-company">{{ expressCompany }}</view>
<view class="express-number">运单号{{ wuliuNo }}</view>
</view>
</view>
<!-- 物流轨迹列表 -->
<view class="trace-list">
<view
v-for="(item, index) in traceList"
:key="index"
class="trace-item"
:class="{ 'active': index === 0 }"
>
<view class="trace-dot">
<view class="dot-inner"></view>
</view>
<view class="trace-line" v-if="index < traceList.length - 1"></view>
<view class="trace-content">
<view class="trace-desc">{{ item.description }}</view>
<view class="trace-time">{{ item.time }}</view>
<view class="trace-location">{{ item.site }}</view>
</view>
</view>
</view>
<!-- 加载状态 -->
<view v-if="loading" class="loading">
<uni-load-more status="loading" :content-text="loadingText"></uni-load-more>
</view>
<!-- 无数据状态 -->
<view v-if="!loading && traceList.length === 0" class="empty-state">
<image src="/static/empty-logistics.png" class="empty-icon" mode="aspectFit"></image>
<text class="empty-text">暂无物流信息</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
wuliuNo: '',
expressCompany: '',
traceList: [],
loading: false,
loadingText: {
contentdown: '上拉显示更多',
contentrefresh: '正在加载...',
contentnomore: '没有更多数据了'
}
}
},
onLoad(options) {
if (options.wliuNo) {
this.wuliuNo = options.wliuNo
}
if (options.expressCompany) {
this.expressCompany = options.expressCompany
}
this.fetchLogisticsTrace()
},
methods: {
goBack() {
uni.navigateBack()
},
async fetchLogisticsTrace() {
if (!this.wuliuNo) {
uni.showToast({ title: '运单号不能为空', icon: 'none' })
return
}
this.loading = true
try {
this.$api('queryTrace', {
wuliuNo: this.wuliuNo
}, res => {
this.loading = false
if (res && res.code === 200 && res.result && res.result.responseParam) {
const traceData = res.result.responseParam
this.traceList = traceData.trace_list || []
if (this.traceList.length === 0) {
uni.showToast({ title: '暂无物流信息', icon: 'none' })
}
} else {
uni.showToast({ title: res?.message || '获取物流信息失败', icon: 'none' })
}
})
} catch (error) {
this.loading = false
uni.showToast({ title: '网络错误,请重试', icon: 'none' })
}
}
}
}
</script>
<style lang="scss" scoped>
.logistics-page {
min-height: 100vh;
background: #f8f8f8;
padding-bottom: 40rpx;
}
.nav-bar {
display: flex;
align-items: center;
height: calc(150rpx + var(--status-bar-height));
padding: 0 32rpx;
padding-top: var(--status-bar-height);
background: #fff;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 999;
box-sizing: border-box;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.03);
.back {
padding: 20rpx;
margin-left: -20rpx;
}
.nav-title {
flex: 1;
text-align: center;
font-size: 32rpx;
font-weight: 500;
color: #222;
}
}
.logistics-card {
margin: calc(150rpx + var(--status-bar-height) + 24rpx) 24rpx 24rpx 24rpx;
background: #fff;
border-radius: 24rpx;
padding: 32rpx;
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.04);
.express-info {
.express-company {
font-size: 32rpx;
font-weight: bold;
color: #222;
margin-bottom: 12rpx;
}
.express-number {
font-size: 28rpx;
color: #666;
}
}
}
.trace-list {
margin: 0 24rpx;
background: #fff;
border-radius: 24rpx;
padding: 32rpx;
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.04);
position: relative;
}
.trace-item {
display: flex;
align-items: flex-start;
position: relative;
padding-bottom: 32rpx;
&:last-child {
padding-bottom: 0;
}
&.active {
.trace-dot {
background: #ff9c00;
border-color: #ff9c00;
}
.trace-content {
.trace-desc {
color: #222;
font-weight: bold;
}
.trace-time {
color: #ff9c00;
}
}
}
.trace-dot {
width: 24rpx;
height: 24rpx;
border-radius: 50%;
background: #ddd;
border: 4rpx solid #f0f0f0;
margin-right: 24rpx;
margin-top: 8rpx;
flex-shrink: 0;
position: relative;
z-index: 2;
.dot-inner {
width: 8rpx;
height: 8rpx;
border-radius: 50%;
background: #fff;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
.trace-line {
position: absolute;
left: 12rpx;
top: 32rpx;
width: 2rpx;
height: calc(100% - 32rpx);
background: #f0f0f0;
z-index: 1;
}
.trace-content {
flex: 1;
min-width: 0;
.trace-desc {
font-size: 28rpx;
color: #666;
line-height: 1.5;
margin-bottom: 8rpx;
word-break: break-all;
}
.trace-time {
font-size: 24rpx;
color: #999;
margin-bottom: 4rpx;
}
.trace-location {
font-size: 24rpx;
color: #bbb;
}
}
}
.loading {
margin: 40rpx 24rpx;
text-align: center;
}
.empty-state {
margin: 120rpx 24rpx;
text-align: center;
.empty-icon {
width: 200rpx;
height: 200rpx;
margin-bottom: 24rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
}
</style>

Loading…
Cancel
Save