一、组件LogisticsTracker
<template>
<view class="logistics-tracker">
<view class="logistics-header">
<view class="logistics-title">物流追踪</view>
<view class="logistics-number">运单号:{{ trackingNumber }}</view>
</view>
<view class="logistics-timeline">
<view
v-for="(item, index) in logisticsInfo"
:key="index"
class="logistics-item"
:class="{ 'logistics-item-active': index === 0 }"
>
<view class="logistics-item-left">
<view class="logistics-dot"></view>
<view
v-if="index !== logisticsInfo.length - 1"
class="logistics-line"
></view>
</view>
<view class="logistics-item-right">
<view class="logistics-status">{{ item.status }}</view>
<view class="logistics-desc">{{ item.description }}</view>
<view class="logistics-info">
<text class="logistics-time">{{ item.time }}</text>
<text class="logistics-location">{{ item.location }}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, defineProps } from "vue";
// 物流信息接口定义
interface LogisticsItem {
status: string; // 物流状态
description: string; // 物流描述
time: string; // 时间
location: string; // 地点
}
// 组件属性定义
const props = defineProps({
trackingNumber: {
type: String,
required: true,
},
logisticsInfo: {
type: Array as () => LogisticsItem[],
required: true,
},
});
</script>
<style lang="scss">
.logistics-tracker {
background-color: #fff;
border-radius: 12rpx;
padding: 30rpx;
margin: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
.logistics-header {
padding-bottom: 30rpx;
border-bottom: 1rpx solid #f5f5f5;
margin-bottom: 30rpx;
.logistics-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 10rpx;
}
.logistics-number {
font-size: 26rpx;
color: #666;
}
}
.logistics-timeline {
.logistics-item {
display: flex;
position: relative;
&:last-child {
.logistics-line {
display: none;
}
}
.logistics-item-left {
width: 40rpx;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
.logistics-dot {
width: 20rpx;
height: 20rpx;
border-radius: 50%;
background-color: #ddd;
z-index: 2;
}
.logistics-line {
width: 2rpx;
flex: 1;
background-color: #ddd;
margin-top: 4rpx;
z-index: 1;
}
}
.logistics-item-right {
flex: 1;
padding: 0 0 40rpx 20rpx;
.logistics-status {
font-size: 28rpx;
font-weight: 500;
color: #333;
margin-bottom: 10rpx;
line-height: 1.2;
}
.logistics-desc {
font-size: 26rpx;
color: #666;
margin-bottom: 10rpx;
line-height: 1.4;
}
.logistics-info {
font-size: 24rpx;
color: #999;
display: flex;
justify-content: space-between;
.logistics-time {
margin-right: 20rpx;
}
}
}
&-active {
.logistics-item-left {
.logistics-dot {
width: 24rpx;
height: 24rpx;
background-color: #2979ff;
}
}
.logistics-item-right {
.logistics-status {
color: #2979ff;
font-weight: bold;
}
}
}
}
}
}
</style>
二、使用logistics
<template>
<view class="logistics-page">
<logistics-tracker
:tracking-number="trackingNumber"
:logistics-info="logisticsData"
/>
</view>
</template>
<script lang="ts" setup>
import { ref } from "vue";
// 组件路径根据自己实际项目引入
import LogisticsTracker from "../../components/LogisticsTracker.vue";
// 物流单号
const trackingNumber = ref("SF123xxxxxxxx23");
// 物流信息数据
const logisticsData = ref([
{
status: "已签收",
description: "您的快递已由前台代签收,感谢您使用顺丰快递,期待再次为您服务",
time: "2023-07-15 14:23:05",
location: "北京市朝阳区",
},
{
status: "派送中",
description: "快递员 张xx (电话: 138xxxx5678) 正在为您派送",
time: "2023-07-15 09:10:25",
location: "北京市朝阳区",
},
{
status: "运输中",
description: "快件已到达 北京朝阳集散中心",
time: "2023-07-14 20:15:37",
location: "北京市朝阳区",
},
{
status: "运输中",
description: "快件已从 上海浦东集散中心 发出",
time: "2023-07-13 18:30:42",
location: "上海市浦东新区",
},
{
status: "已揽收",
description: "顺丰快递员已揽收",
time: "2023-07-13 14:20:16",
location: "上海市浦东新区",
},
]);
</script>
<style lang="scss">
.logistics-page {
min-height: 100vh;
background-color: #f5f5f5;
padding: 20rpx 0;
}
</style>
最终样式

11.png
样式根据设计稿可做调整