2025-12-16 09:13:48 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<view class="content">
|
|
|
|
|
|
<TopSafe></TopSafe>
|
|
|
|
|
|
<Header title="物流信息" />
|
2026-09-05 16:11:42 +08:00
|
|
|
|
|
|
|
|
|
|
<view class="main-body">
|
|
|
|
|
|
<!-- 多物流 Tab 切换 (数据源:this.expressList) -->
|
|
|
|
|
|
<view class="tabs-bar" v-if="expressList && expressList.length > 0">
|
|
|
|
|
|
<scroll-view class="tabs-scroll" scroll-x :show-scrollbar="false">
|
|
|
|
|
|
<view class="tabs-wrapper">
|
|
|
|
|
|
<view
|
|
|
|
|
|
v-for="(item, index) in expressList"
|
|
|
|
|
|
:key="index"
|
|
|
|
|
|
:class="['tab-item', { active: currentTab === index }]"
|
|
|
|
|
|
@click="onSelectTab(index)"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ getTabName(index) }}
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</scroll-view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 物流详情面板 -->
|
2025-12-16 09:13:48 +08:00
|
|
|
|
<view class="express-panel">
|
2026-09-05 16:11:42 +08:00
|
|
|
|
<!-- 快递单号行 -->
|
|
|
|
|
|
<view class="info-row">
|
|
|
|
|
|
<view class="icon-wrap">
|
|
|
|
|
|
<u-icon name="bag" color="#333333" size="20"></u-icon>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<text class="info-label" v-if="displayExpressName">{{ displayExpressName }}:</text>
|
|
|
|
|
|
<text class="info-label" v-else>快递单号:</text>
|
|
|
|
|
|
<text class="info-val">{{ displayExpressNo || '--' }}</text>
|
|
|
|
|
|
<view class="copy-btn" v-if="displayExpressNo" @click="copyData(displayExpressNo)">
|
|
|
|
|
|
<up-image src="/static/common/copy.png" width="14" height="14" bgColor="#f1f6ff00"></up-image>
|
|
|
|
|
|
</view>
|
2025-12-16 09:13:48 +08:00
|
|
|
|
</view>
|
2026-09-05 16:11:42 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 收货地址行 -->
|
|
|
|
|
|
<view class="info-row align-start" style="margin-top: 24rpx;" v-if="displayAddress">
|
|
|
|
|
|
<view class="icon-wrap" style="margin-top: 4rpx;">
|
|
|
|
|
|
<u-icon name="map" color="#333333" size="20"></u-icon>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<text class="info-label">收货地址:</text>
|
|
|
|
|
|
<text class="info-val address-text">{{ displayAddress }}</text>
|
2025-12-16 09:13:48 +08:00
|
|
|
|
</view>
|
2026-09-05 16:11:42 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 分割线 -->
|
|
|
|
|
|
<view class="divider"></view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 物流轨迹时间轴 -->
|
|
|
|
|
|
<view class="trace-list" v-if="express && express.logisticsTraceDetailList && express.logisticsTraceDetailList.length > 0">
|
|
|
|
|
|
<view
|
|
|
|
|
|
class="trace-item"
|
|
|
|
|
|
v-for="(item, index) in express.logisticsTraceDetailList"
|
|
|
|
|
|
:key="index"
|
|
|
|
|
|
>
|
|
|
|
|
|
<!-- 左侧时间轴 (点 + 连接线) -->
|
|
|
|
|
|
<view class="axis-col">
|
|
|
|
|
|
<view :class="['dot', { 'is-latest': index === 0 }]"></view>
|
|
|
|
|
|
<view class="line" v-if="index < express.logisticsTraceDetailList.length - 1"></view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 右侧轨迹内容 -->
|
|
|
|
|
|
<view class="content-col">
|
|
|
|
|
|
<view class="trace-header">
|
|
|
|
|
|
<text :class="['trace-status', { 'is-latest': index === 0 }]">
|
|
|
|
|
|
{{ getTraceTitle(item, index) }}
|
|
|
|
|
|
</text>
|
|
|
|
|
|
<text class="trace-time">{{ item.curTime || item.time }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="trace-desc" v-if="item.desc">
|
|
|
|
|
|
{{ item.desc }}
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 空数据提示 -->
|
|
|
|
|
|
<view class="empty-box" v-else>
|
|
|
|
|
|
<text class="empty-text">暂无物流轨迹信息</text>
|
|
|
|
|
|
</view>
|
2025-12-16 09:13:48 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2026-09-05 16:11:42 +08:00
|
|
|
|
|
2025-12-16 09:13:48 +08:00
|
|
|
|
<up-toast ref="uToastRef"></up-toast>
|
|
|
|
|
|
<!-- 腾讯电子签H5 → 集成到UniApp -->
|
2026-09-05 16:11:42 +08:00
|
|
|
|
<qiaobao-assistant page-key="pages/rwa_package/express/express" title="物流详情" />
|
|
|
|
|
|
</view>
|
2025-12-16 09:13:48 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import Header from '@/components/common/header.vue';
|
2026-09-05 16:11:42 +08:00
|
|
|
|
import TopSafe from '@/components/common/top-safe.nvue';
|
|
|
|
|
|
import MyBtn from '@/components/common/my-btn.vue';
|
2025-12-16 09:13:48 +08:00
|
|
|
|
import func from '@/utils/func.js';
|
|
|
|
|
|
import { copyData } from '@/utils/index.js';
|
2026-09-05 16:11:42 +08:00
|
|
|
|
import { getDetail, orderLogistics } from "@/api/order.js";
|
2025-12-16 09:13:48 +08:00
|
|
|
|
|
|
|
|
|
|
export default {
|
2026-09-05 16:11:42 +08:00
|
|
|
|
components: { Header, TopSafe, MyBtn },
|
2025-12-16 09:13:48 +08:00
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
2026-09-05 16:11:42 +08:00
|
|
|
|
express: null,
|
|
|
|
|
|
curData: null,
|
|
|
|
|
|
id: 0,
|
|
|
|
|
|
expressList: [],
|
|
|
|
|
|
expressId: '',
|
|
|
|
|
|
currentTab: 0,
|
2025-12-16 09:13:48 +08:00
|
|
|
|
};
|
|
|
|
|
|
},
|
2026-09-05 16:11:42 +08:00
|
|
|
|
computed: {
|
|
|
|
|
|
currentPackage() {
|
|
|
|
|
|
return this.expressList[this.currentTab] || {};
|
|
|
|
|
|
},
|
|
|
|
|
|
displayExpressName() {
|
|
|
|
|
|
return this.currentPackage.expressName || this.curData?.items?.[0]?.expressName || this.express?.expressName || '';
|
|
|
|
|
|
},
|
|
|
|
|
|
displayExpressNo() {
|
|
|
|
|
|
return this.currentPackage.expressNo || this.expressId || this.curData?.items?.[0]?.expressNo || '';
|
|
|
|
|
|
},
|
|
|
|
|
|
displayAddress() {
|
|
|
|
|
|
if (!this.curData) return '';
|
|
|
|
|
|
const parts = [
|
|
|
|
|
|
this.curData.countryName,
|
|
|
|
|
|
this.curData.cityName,
|
|
|
|
|
|
this.curData.areaName,
|
|
|
|
|
|
this.curData.address
|
|
|
|
|
|
].filter(Boolean);
|
|
|
|
|
|
return parts.join(' ');
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2025-12-16 09:13:48 +08:00
|
|
|
|
onLoad(option) {
|
2026-09-05 16:11:42 +08:00
|
|
|
|
if (option) {
|
|
|
|
|
|
if (option.id) {
|
|
|
|
|
|
this.id = Number(option.id);
|
|
|
|
|
|
this.detail();
|
|
|
|
|
|
} else if (option.expressNo) {
|
|
|
|
|
|
this.expressId = option.expressNo;
|
|
|
|
|
|
this.expressList = [{ expressNo: option.expressNo }];
|
|
|
|
|
|
this.getOrderLogistics();
|
|
|
|
|
|
}
|
2025-12-16 09:13:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
copyData,
|
2026-09-05 16:11:42 +08:00
|
|
|
|
|
|
|
|
|
|
getTabName(index) {
|
|
|
|
|
|
const zhNums = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十'];
|
|
|
|
|
|
return `物流${zhNums[index] || (index + 1)}`;
|
2025-12-16 09:13:48 +08:00
|
|
|
|
},
|
2026-09-05 16:11:42 +08:00
|
|
|
|
|
|
|
|
|
|
getTraceTitle(item, index) {
|
|
|
|
|
|
if (item.areaName) return item.areaName;
|
|
|
|
|
|
if (item.statusName) return item.statusName;
|
|
|
|
|
|
if (item.logisticsStatusDesc) return item.logisticsStatusDesc;
|
|
|
|
|
|
if (item.action) return item.action;
|
|
|
|
|
|
if (item.status) return item.status;
|
|
|
|
|
|
return index === 0 ? '已签收' : '运输中';
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
onSelectTab(index) {
|
|
|
|
|
|
this.currentTab = index;
|
|
|
|
|
|
const pkg = this.expressList[index];
|
|
|
|
|
|
if (pkg && pkg.expressNo) {
|
|
|
|
|
|
this.expressId = pkg.expressNo;
|
|
|
|
|
|
this.getOrderLogistics();
|
2025-12-16 09:13:48 +08:00
|
|
|
|
}
|
2026-09-05 16:11:42 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
detail() {
|
|
|
|
|
|
let params = {
|
|
|
|
|
|
id: this.id
|
|
|
|
|
|
};
|
|
|
|
|
|
getDetail(params).then(res => {
|
|
|
|
|
|
if (res.bizcode == 100) {
|
2025-12-16 09:13:48 +08:00
|
|
|
|
this.curData = res.data;
|
2026-09-05 16:11:42 +08:00
|
|
|
|
this.expressList = this.curData?.orderPackages || [];
|
|
|
|
|
|
if (this.expressList.length > 0) {
|
|
|
|
|
|
this.onSelectTab(0);
|
|
|
|
|
|
} else if (this.curData?.items?.[0]?.expressNo) {
|
|
|
|
|
|
this.expressList = [{
|
|
|
|
|
|
expressName: this.curData.items[0].expressName,
|
|
|
|
|
|
expressNo: this.curData.items[0].expressNo
|
|
|
|
|
|
}];
|
|
|
|
|
|
this.onSelectTab(0);
|
2025-12-16 09:13:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-09-05 16:11:42 +08:00
|
|
|
|
});
|
2025-12-16 09:13:48 +08:00
|
|
|
|
},
|
2026-09-05 16:11:42 +08:00
|
|
|
|
|
|
|
|
|
|
getOrderLogistics() {
|
|
|
|
|
|
if (!this.expressId) return;
|
2025-12-16 09:13:48 +08:00
|
|
|
|
let params = {
|
2026-09-05 16:11:42 +08:00
|
|
|
|
mailNo: this.expressId
|
|
|
|
|
|
};
|
|
|
|
|
|
orderLogistics(params).then(res => {
|
|
|
|
|
|
if (res.bizcode == 100 && res.data) {
|
2025-12-16 09:13:48 +08:00
|
|
|
|
this.express = res.data;
|
2026-09-05 16:11:42 +08:00
|
|
|
|
if (Array.isArray(this.express.logisticsTraceDetailList)) {
|
|
|
|
|
|
this.express.logisticsTraceDetailList.forEach(item => {
|
|
|
|
|
|
if (item.time) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
item.curTime = func.formatDate(new Date(item.time), 2);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
item.curTime = item.time;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.express = null;
|
2025-12-16 09:13:48 +08:00
|
|
|
|
}
|
2026-09-05 16:11:42 +08:00
|
|
|
|
});
|
2025-12-16 09:13:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
2026-09-05 16:11:42 +08:00
|
|
|
|
.content {
|
|
|
|
|
|
width: 100vw;
|
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
|
background-color: #ffffff;
|
2025-12-16 09:13:48 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
2026-09-05 16:11:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.main-body {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tabs-bar {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
padding: 24rpx 32rpx 16rpx 32rpx;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
background-color: #ffffff;
|
|
|
|
|
|
|
|
|
|
|
|
.tabs-scroll {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
|
|
|
|
|
|
.tabs-wrapper {
|
|
|
|
|
|
display: inline-flex;
|
2025-12-16 09:13:48 +08:00
|
|
|
|
flex-direction: row;
|
|
|
|
|
|
align-items: center;
|
2026-09-05 16:11:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tab-item {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
padding: 12rpx 36rpx;
|
|
|
|
|
|
border-radius: 32rpx;
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
color: #333333;
|
|
|
|
|
|
background-color: #F5F5F5;
|
|
|
|
|
|
margin-right: 24rpx;
|
|
|
|
|
|
transition: all 0.2s ease;
|
|
|
|
|
|
|
|
|
|
|
|
&.active {
|
|
|
|
|
|
background-color: #F2E9FF;
|
|
|
|
|
|
color: #7934F6;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.express-panel {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
padding: 20rpx 32rpx 40rpx 32rpx;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
|
|
|
|
|
|
.info-row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
|
|
|
|
&.align-start {
|
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.icon-wrap {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
margin-right: 12rpx;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-label {
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
color: #333333;
|
|
|
|
|
|
margin-right: 12rpx;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-val {
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
color: #333333;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
|
|
|
|
|
|
&.address-text {
|
|
|
|
|
|
font-weight: normal;
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.copy-btn {
|
|
|
|
|
|
margin-left: 16rpx;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.divider {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 1rpx;
|
|
|
|
|
|
background-color: #F5F5F5;
|
|
|
|
|
|
margin: 32rpx 0 40rpx 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.trace-list {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
|
|
|
|
|
|
.trace-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
padding-bottom: 44rpx;
|
|
|
|
|
|
|
|
|
|
|
|
&:last-child {
|
|
|
|
|
|
padding-bottom: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.axis-col {
|
|
|
|
|
|
width: 36rpx;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
|
|
|
|
|
|
.dot {
|
|
|
|
|
|
width: 16rpx;
|
|
|
|
|
|
height: 16rpx;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
border: 3rpx solid #CCCCCC;
|
|
|
|
|
|
background-color: #FFFFFF;
|
|
|
|
|
|
z-index: 2;
|
|
|
|
|
|
margin-top: 8rpx;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
|
|
|
|
|
|
&.is-latest {
|
|
|
|
|
|
width: 20rpx;
|
|
|
|
|
|
height: 20rpx;
|
|
|
|
|
|
background-color: #7934F6;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
margin-top: 6rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.line {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 24rpx;
|
|
|
|
|
|
bottom: -10rpx;
|
|
|
|
|
|
left: 50%;
|
|
|
|
|
|
transform: translateX(-50%);
|
|
|
|
|
|
width: 2rpx;
|
|
|
|
|
|
background-color: #EEEEEE;
|
|
|
|
|
|
z-index: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.content-col {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
margin-left: 20rpx;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
|
|
|
|
|
|
.trace-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
|
|
|
|
.trace-status {
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
color: #333333;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
|
|
|
|
|
|
&.is-latest {
|
|
|
|
|
|
color: #7934F6;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.trace-time {
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
color: #999999;
|
|
|
|
|
|
margin-left: 20rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.trace-desc {
|
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
|
color: #666666;
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
margin-top: 12rpx;
|
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.empty-box {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
padding: 60rpx 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
|
|
|
|
.empty-text {
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
color: #999999;
|
2025-12-16 09:13:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|
2026-09-05 16:11:42 +08:00
|
|
|
|
|