feat:0905优惠券多物流信息

This commit is contained in:
2026-09-05 16:11:42 +08:00
parent b1d3ec9b2f
commit 75060faa39
14 changed files with 825 additions and 233 deletions
+389 -81
View File
@@ -2,131 +2,439 @@
<view class="content">
<TopSafe></TopSafe>
<Header title="物流信息" />
<view class="setting-view">
<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>
<!-- 物流详情面板 -->
<view class="express-panel">
<view class="top-one" style="margin-top:12rpx">
<u-icon name="email" color="#333" size="20"></u-icon>
<text class="text-24 text-fu text-space" style="margin-left:10rpx;line-height: 1.4;margin-right:12rpx">{{curData && `${curData.items[0].expressName}:`}}</text>
<text class="text-24 text-gray text-space" style="margin-left:10rpx;line-height: 1.4;margin-right:12rpx">{{curData && `${curData.items[0].expressNo}`}}</text>
<up-image src="/static/common/copy.png" width="14" height="14" bgColor="#f1f6ff00"
@click="copyData(curData ? curData.items[0].expressNo :'')"></up-image>
<!-- 快递单号行 -->
<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>
</view>
<view class="top-one" style="border-bottom:2rpx solid #eee">
<u-icon name="map" color="#333" size="20"></u-icon>
<text class="text-24 text-fu text-space" style="margin-left:10rpx;line-height: 1.4;margin-right:12rpx">收货地址</text>
<text class="text-24 text-zu text-space" style="margin-left:10rpx;line-height: 1.4;margin-right:12rpx">{{curData && `${curData.countryName} ${curData.cityName} ${curData.areaName} ${curData.address}`}}</text>
<!-- 收货地址行 -->
<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>
</view>
<u-steps current="0" direction="column" dot activeColor="#7934F6" style="margin-top:20rpx">
<u-steps-item :style="`width:calc(100vw - 48rpx)`"
:title="item.areaName + ' ' + item.curTime" :desc="item.desc" v-for="(item,index) in express.logisticsTraceDetailList" :key="index"></u-steps-item>
</u-steps>
<!-- 分割线 -->
<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>
</view>
</view>
<up-toast ref="uToastRef"></up-toast>
<!-- 腾讯电子签H5 → 集成到UniApp -->
<qiaobao-assistant page-key="pages/rwa_package/express/express" title="物流详情" />
</view>
<qiaobao-assistant page-key="pages/rwa_package/express/express" title="物流详情" />
</view>
</template>
<script>
import { getUserInfo,uploadAvatar } from '@/api/auth.js';
import { uploadImage, uploadImg } from '@/api/common.js';
import { IMG_TYPE } from '@/utils/enumUtils.js';
import { BASE_URL, Authorization } from '@/utils/config.js';
import { getStorageFun, TOKEN_NAME,clear } from '@/utils/auth.js';
import Header from '@/components/common/header.vue';
import TopSafe from '@/components/common/top-safe.nvue'
import MyBtn from '@/components/common/my-btn.vue'
import TopSafe from '@/components/common/top-safe.nvue';
import MyBtn from '@/components/common/my-btn.vue';
import func from '@/utils/func.js';
import { copyData } from '@/utils/index.js';
import {
getDetail,
orderLogistics
} from "@/api/order.js";
import { getDetail, orderLogistics } from "@/api/order.js";
export default {
components: { Header, TopSafe,MyBtn },
components: { Header, TopSafe, MyBtn },
data() {
return {
express:null,
curData:null,
id:0
express: null,
curData: null,
id: 0,
expressList: [],
expressId: '',
currentTab: 0,
};
},
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(' ');
}
},
onLoad(option) {
// this.userinfo();
if(option && option.id){
this.id = Number(option.id);
this.init();
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();
}
}
},
methods: {
copyData,
init() {
this.detail();
getTabName(index) {
const zhNums = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十'];
return `物流${zhNums[index] || (index + 1)}`;
},
detail(){
let params = {
id:this.id
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();
}
getDetail(params).then(res=>{
if(res.bizcode == 100){
},
detail() {
let params = {
id: this.id
};
getDetail(params).then(res => {
if (res.bizcode == 100) {
this.curData = res.data;
if(this.curData.items[0].expressNo){
this.getOrderLogistics();
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);
}
}
console.log('id',res)
})
});
},
getOrderLogistics(){
getOrderLogistics() {
if (!this.expressId) return;
let params = {
mailNo:this.curData.items[0].expressNo
}
orderLogistics(params).then(res=>{
if(res.bizcode == 100){
mailNo: this.expressId
};
orderLogistics(params).then(res => {
if (res.bizcode == 100 && res.data) {
this.express = res.data;
this.express.logisticsTraceDetailList.forEach(item=>{
item.curTime = func.formatDate(new Date(item.time), 2);
})
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;
}
})
});
}
}
}
</script>
<style lang="scss" scoped>
.setting-view {
width: 100%;
height: calc(100% - 88rpx - var(--status-bar-height));
.content {
width: 100vw;
min-height: 100vh;
background-color: #ffffff;
display: flex;
flex-direction: column;
align-items: center;
.express-panel{
width:100%;
margin-top:24rpx;
height: calc(100% - 24rpx);
padding: 0 32rpx;
box-sizing: border-box;
overflow-y: auto;
display: flex;
flex-direction: column;
background-color: #fff;
.top-one{
width:100%;
display: flex;
}
.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;
flex-direction: row;
align-items: center;
min-height:70rpx;
}
.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;
}
}
}
</style>