张田田 1 tháng trước cách đây
mục cha
commit
1cf8bb3558

+ 2 - 1
miniprogram/app.json

@@ -100,7 +100,8 @@
         "pages/goods-evaluate/goods-evaluate",
         "pages/goods-evaluateDetail/goods-evaluateDetail",
         "pages/offline-evaluate/offline-evaluate",
-        "pages/offline-evaluateDetail/offline-evaluateDetail"
+        "pages/offline-evaluateDetail/offline-evaluateDetail",
+        "pages/logistics-detail/logistics-detail"
       ]
     }
   ],

+ 10 - 0
miniprogram/module/order/pages/logistics-detail/logistics-detail.json

@@ -0,0 +1,10 @@
+{
+  "renderer": "skyline",
+  "component": true,
+  "usingComponents": {
+    "t-navbar": "tdesign-miniprogram/navbar/navbar",
+    "t-icon": "tdesign-miniprogram/icon/icon",
+    "t-tag": "tdesign-miniprogram/tag/tag",
+    "t-image": "tdesign-miniprogram/image/image"
+  }
+}

+ 385 - 0
miniprogram/module/order/pages/logistics-detail/logistics-detail.scss

@@ -0,0 +1,385 @@
+@import "../../../../themes/t.cell.scss";
+@import "../../../../themes/page.scss";
+@import "../../assets/iconfont/iconfont.wxss";
+
+/* module/order/pages/logistics-detail/logistics-detail.scss */
+.page-scroll__container {
+  flex: 0 1 auto;
+  height: var(--page-container-safeHeight, 100vh);
+  background: #f5f5f5;
+  padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
+  box-sizing: border-box;
+  padding-top: 10px;
+}
+
+.logistics-page {
+  padding-bottom: 32rpx;
+}
+
+.map-header {
+  height: 320rpx;
+  background: linear-gradient(180deg, #dde6f0 0%, #e8edf4 45%, #f0f2f5 100%);
+  position: relative;
+  overflow: hidden;
+}
+
+.map-header__inner {
+  position: absolute;
+  inset: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  opacity: 0.85;
+}
+
+.map-header__inner::before {
+  content: "";
+  position: absolute;
+  left: 10%;
+  right: 10%;
+  top: 38%;
+  height: 2rpx;
+  background: rgba(255, 255, 255, 0.7);
+  transform: rotate(-8deg);
+}
+
+.map-header__inner::after {
+  content: "";
+  position: absolute;
+  left: 20%;
+  right: 15%;
+  top: 55%;
+  height: 2rpx;
+  background: rgba(255, 255, 255, 0.5);
+  transform: rotate(6deg);
+}
+
+.map-header__city {
+  font-size: 28rpx;
+  color: rgba(0, 0, 0, 0.35);
+  font-weight: 500;
+}
+
+.logistics-card {
+  margin: -48rpx 24rpx 0;
+  padding: 32rpx 28rpx 28rpx;
+  background: #fff;
+  border-radius: 16rpx;
+  box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.06);
+  position: relative;
+  z-index: 1;
+}
+
+.carrier-row {
+  display: flex;
+  align-items: center;
+  margin-bottom: 28rpx;
+}
+
+.carrier-row__logo {
+  width: 50rpx;
+  height: 50rpx;
+  border-radius: 50%;
+  background: #1e6ee8;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-shrink: 0;
+}
+
+.carrier-row__logo-text {
+  font-size: 22rpx;
+  color: #fff;
+  font-weight: 600;
+}
+
+.carrier-row__info {
+  flex: 1;
+  min-width: 0;
+  margin-left: 20rpx;
+  display: flex;
+  gap: 8rpx;
+}
+
+.carrier-row__name {
+  font-size: 24rpx;
+  font-weight: 600;
+  color: #888;
+  margin-right: 3px;
+}
+
+.carrier-row__no {
+  font-size: 24rpx;
+  color: #888;
+}
+
+.carrier-row__actions {
+  display: flex;
+  align-items: center;
+  flex-shrink: 0;
+  color: #888;
+}
+
+.carrier-row__action {
+  font-size: 26rpx;
+  padding: 8rpx 12rpx;
+}
+
+.carrier-row__divider {
+  width: 1rpx;
+  height: 28rpx;
+  background: #e5e5e5;
+  margin: 0 4rpx;
+}
+
+/* 物流时间线:左侧虚线轴 */
+.trace-list {
+  position: relative;
+}
+
+.trace-list__axis {
+  position: absolute;
+  left: 16rpx;
+  top: 22rpx;
+  bottom: 26rpx;
+  width: 0;
+  border-left: 2rpx dashed #d8d8d8;
+}
+
+.trace-item {
+  position: relative;
+  padding-left: 48rpx;
+  padding-bottom: 8rpx;
+}
+
+.trace-item--current {
+  padding-bottom: 16rpx;
+}
+
+.trace-item--expand {
+  padding-bottom: 0;
+  padding-top: 8rpx;
+}
+
+.trace-item__dot {
+  position: absolute;
+  left: 16rpx;
+  top: 10rpx;
+  width: 16rpx;
+  height: 16rpx;
+  border-radius: 50%;
+  transform: translateX(-50%);
+  z-index: 1;
+}
+
+.trace-item__dot--active {
+  background: #ff6b00;
+  box-shadow: 0 0 0 6rpx rgba(255, 107, 0, 0.22);
+}
+
+.trace-item__dot--grey {
+  background: #c8c8c8;
+}
+
+.trace-item__dot--hollow {
+  width: 14rpx;
+  height: 14rpx;
+  top: 12rpx;
+  border: 2rpx solid #bbb;
+  background: #fff;
+}
+
+.trace-item__body--inline {
+  display: flex;
+  align-items: center;
+  gap: 8rpx;
+}
+
+.trace-item__title-row {
+  display: flex;
+  align-items: baseline;
+  flex-wrap: wrap;
+  gap: 16rpx;
+  margin-bottom: 12rpx;
+}
+
+.trace-item__status {
+  font-size: 28rpx;
+  font-weight: 600;
+  color: #333;
+}
+
+.trace-item__status--accent {
+  font-size: 32rpx;
+  font-weight: 700;
+  color: #ff6b00;
+}
+
+.trace-item__time {
+  font-size: 24rpx;
+  color: #999;
+  margin-left: 8px;
+}
+
+.trace-item__time--accent {
+  font-size: 26rpx;
+  color: #ff6b00;
+}
+
+.trace-item__desc {
+  font-size: 26rpx;
+  color: #888;
+  line-height: 1.55;
+  display: block;
+}
+
+.trace-item__desc--muted {
+  color: #888;
+}
+
+.trace-expand__text {
+  font-size: 26rpx;
+  color: #999;
+  flex: 1;
+}
+
+.logistics-divider {
+  height: 1rpx;
+  background: #eee;
+  margin: 28rpx 0 24rpx;
+}
+
+.addr-block {
+  display: flex;
+  align-items: flex-start;
+  gap: 12rpx;
+}
+
+.addr-block__icon {
+  flex-shrink: 0;
+  margin-top: 6rpx;
+}
+
+.addr-block__main {
+  flex: 1;
+  min-width: 0;
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+  margin-left: 5px;
+}
+
+.addr-block__address {
+  font-size: 28rpx;
+  font-weight: 600;
+  color: #333;
+  line-height: 1.45;
+  width: 100%;
+}
+
+.addr-block__contact {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 12rpx 16rpx;
+  width: 100%;
+  color: #888;
+  font-size: 22rpx;
+}
+
+.address-name {
+  font-weight: 500;
+  line-height: 1.4;
+  margin-right: 10rpx;
+}
+
+.address-phone {
+  font-weight: 400;
+  line-height: 1.4;
+}
+
+.addr-block__tag {
+  margin: 0 0rpx 0 10rpx !important;
+  font-size: 22rpx!important;
+  color: #888!important;
+}
+
+.addr-block__tag--orange {
+  color: #ff6b00 !important;
+  border-color: rgba(255, 107, 0, 0.45) !important;
+}
+
+.addr-block__virtual {
+  display: flex;
+  align-items: center;
+  gap: 2rpx;
+  font-size: 22rpx;
+}
+
+.addr-block__virtual-arrow {
+  margin-left: -4rpx;
+}
+
+.goods-section {
+  margin: 24rpx 24rpx 0;
+  padding: 8rpx 24rpx 24rpx;
+  background: #fff;
+  border-radius: 16rpx;
+}
+
+.goods-item {
+  display: flex;
+  align-items: flex-start;
+  padding: 24rpx 0;
+  border-bottom: 1rpx solid #f0f0f0;
+}
+
+.goods-item:last-child {
+  border-bottom: none;
+}
+
+.goods-item__img {
+  flex-shrink: 0;
+  margin-right: 20rpx;
+}
+
+.goods-item__main {
+  flex: 1;
+  min-width: 0;
+  display: flex;
+  flex-direction: column;
+  gap: 10rpx;
+}
+
+.goods-item__title {
+  font-size: 28rpx;
+  color: #333;
+  line-height: 1.4;
+  margin-bottom: 10px;
+}
+
+.goods-item__spec {
+  font-size: 24rpx;
+  color: #999;
+}
+
+.goods-item__price-col {
+  flex-shrink: 0;
+  text-align: right;
+  display: flex;
+  flex-direction: column;
+  gap: 12rpx;
+  align-items: flex-end;
+}
+
+.goods-item__price {
+  font-size: 28rpx;
+  color: #333;
+  font-weight: 600;
+  margin-bottom: 10px;
+}
+
+.goods-item__qty {
+  font-size: 24rpx;
+  color: #999;
+}

+ 139 - 0
miniprogram/module/order/pages/logistics-detail/logistics-detail.ts

@@ -0,0 +1,139 @@
+import PageContainerBehavior from "../../../../core/behavior/page-container.behavior";
+import DictionariesBehavior from "../../../../core/behavior/dictionaries.behavior";
+import tickleBehavior from "../../../../core/behavior/tickle.behavior";
+
+interface TraceItem {
+  status: string;
+  time: string;
+  desc: string;
+}
+
+interface GoodsRow {
+  id: string;
+  image: string;
+  title: string;
+  spec: string;
+  price: string;
+  quantity: number;
+}
+
+Page({
+  behaviors: [PageContainerBehavior, DictionariesBehavior, tickleBehavior],
+  data: {
+    carrierName: "",
+    carrierIcon: "",
+    expressType: "3",
+    trackingNo: "78969033222603",
+    courierPhone: "95311",
+    showAllTrace: false,
+    latestTrace: {
+      status: "已签收",
+      time: "12-26 18:33",
+      desc: "您已在杭州金成花园15幢4号店完成取件,感谢使用菜鸟驿站,期待再次为您服务。",
+    } as TraceItem,
+    traceHistory: [
+      {
+        status: "待取件",
+        time: "12-26 09:12",
+        desc: "包裹已到达杭州金成花园菜鸟驿站,请凭取件码及时取件。",
+      },
+      {
+        status: "运输中",
+        time: "12-25 14:20",
+        desc: "快件已离开【杭州转运中心】,正在发往下一站。",
+      },
+      {
+        status: "已揽收",
+        time: "12-24 16:05",
+        desc: "快递员已揽收,揽收员:张师傅 138****0000。",
+      },
+    ] as TraceItem[],
+    deliveryAddress: "送至 西湖区金成花园13幢一单元602",
+    recipientName: "何美丹",
+    recipientPhone: "86-158****5026",
+    goodsList: [
+      {
+        id: "1",
+        image: "https://www.bing.com/th/id/OIP.BSEy1twCVmS1ysqly8NZjQHaE8?w=288&h=204&c=8&rs=1&qlt=90&o=6&pid=3.1&rm=2",
+        title: "元气茶",
+        spec: "10包",
+        price: "32",
+        quantity: 3,
+      },
+      {
+        id: "2",
+        image: "https://www.bing.com/th/id/OIP.BSEy1twCVmS1ysqly8NZjQHaE8?w=288&h=204&c=8&rs=1&qlt=90&o=6&pid=3.1&rm=2",
+        title: "芡实米仁燕麦粥",
+        spec: "1袋",
+        price: "1.5",
+        quantity: 21,
+      },
+    ] as GoodsRow[],
+  },
+
+  onLoad(options: Record<string, string>) {
+    const expressType = options.expressType || this.data.expressType;
+    this.setData({
+      expressType,
+      carrierName: this.getExpressTypeName(expressType),
+      carrierIcon: this.getExpressTypeIcon(expressType),
+      trackingNo: this.data.trackingNo,
+    });
+  },
+
+  toggleTraceExpand() {
+    this.setData({ showAllTrace: !this.data.showAllTrace });
+  },
+
+  onCopyTracking() {
+    const no = this.data.trackingNo;
+    wx.setClipboardData({
+      data: no,
+      success: () => {
+        wx.showToast({ title: "已复制运单号", icon: "none" });
+      },
+    });
+  },
+
+  onCallCourier() {
+    const phone = this.data.courierPhone;
+    wx.makePhoneCall({ phoneNumber: phone });
+  },
+
+  onVirtualNumberTip() {
+    wx.showToast({
+      title: "取件时可向驿站出示虚拟号",
+      icon: "none",
+    });
+  },
+
+  // 与快递公司名称
+  getExpressTypeName(expressType?: string | number): string {
+    const expressTypeMap: Record<string, string> = {
+      "0": "邮政速递",
+      "1": "顺丰速运",
+      "2": "京东快递",
+      "3": "中通快递",
+      "4": "圆通速递",
+      "5": "申通快递",
+      "6": "韵达快递",
+      "7": "极兔速递",
+    };
+    return expressTypeMap[String(expressType || "")] || "";
+  },
+
+  // 获取快递图标
+  getExpressTypeIcon(expressType?: string | number): string {
+    const expressIconMap: Record<string, string> = {
+      "0": "icon-youzhengkuaidi",
+      "1": "icon-shunfengkuaidi",
+      "2": "icon-jingdong",
+      "3": "icon-zhongtong",
+      "4": "icon-yuantongkuaidi",
+      "5": "icon-shentong",
+      "6": "icon-yunda",
+      "7": "icon-jitukuaidi",
+    };
+    return expressIconMap[String(expressType || "")] || "";
+  },
+});

+ 95 - 0
miniprogram/module/order/pages/logistics-detail/logistics-detail.wxml

@@ -0,0 +1,95 @@
+<!-- module/order/pages/logistics-detail/logistics-detail.wxml -->
+<t-navbar title="物流详情" left-arrow />
+<scroll-view class="page-scroll__container" type="list" scroll-y style="{{containerStyle}}">
+  <view class="logistics-page">
+    <!-- <view class="map-header">
+      <view class="map-header__inner">
+        <text class="map-header__city">北京</text>
+      </view>
+    </view> -->
+
+    <view class="logistics-card">
+      <view class="carrier-row">
+        <i class="iconfont icon-zhongtong" style="font-size:15px"></i>
+        <view class="carrier-row__info">
+          <text class="carrier-row__name">{{carrierName}}</text>
+          <text class="carrier-row__no">{{trackingNo}}</text>
+        </view>
+        <view class="carrier-row__actions">
+          <text class="carrier-row__action" bindtap="onCopyTracking">复制</text>
+          <view class="carrier-row__divider" />
+          <text class="carrier-row__action" bindtap="onCallCourier">打电话</text>
+        </view>
+      </view>
+
+      <view class="trace-list">
+        <view class="trace-list__axis" />
+
+        <view class="trace-item trace-item--current">
+          <view class="trace-item__dot trace-item__dot--active" />
+          <view class="trace-item__body">
+            <view class="trace-item__title-row">
+              <text class="trace-item__status trace-item__status--accent">{{latestTrace.status}}</text>
+              <text class="trace-item__time trace-item__time--accent">{{latestTrace.time}}</text>
+            </view>
+            <text class="trace-item__desc trace-item__desc--muted">{{latestTrace.desc}}</text>
+          </view>
+        </view>
+
+        <block wx:if="{{showAllTrace}}">
+          <view class="trace-item" wx:for="{{traceHistory}}" wx:key="time">
+            <view class="trace-item__dot trace-item__dot--grey" />
+            <view class="trace-item__body">
+              <view class="trace-item__title-row">
+                <text class="trace-item__status">{{item.status}}</text>
+                <text class="trace-item__time">{{item.time}}</text>
+              </view>
+              <text class="trace-item__desc">{{item.desc}}</text>
+            </view>
+          </view>
+        </block>
+
+        <view class="trace-item trace-item--expand" bindtap="toggleTraceExpand">
+          <view class="trace-item__dot trace-item__dot--hollow" />
+          <view class="trace-item__body trace-item__body--inline">
+            <text class="trace-expand__text">{{showAllTrace ? '收起物流明细' : '查看更多物流明细'}}</text>
+            <t-icon name="{{showAllTrace ? 'chevron-up' : 'chevron-down'}}" size="36rpx" color="#999" />
+          </view>
+        </view>
+      </view>
+
+      <view class="logistics-divider" />
+
+      <view class="addr-block">
+        <t-icon name="location" size="30rpx" color="#333" class="addr-block__icon" />
+        <view class="addr-block__main">
+          <text class="addr-block__address">{{deliveryAddress}}</text>
+          <view class="addr-block__contact">
+            <text class="address-name">{{recipientName}}</text>
+            <text class="address-phone">{{recipientPhone}}</text>
+            <t-tag size="small" variant="outline" theme="default" class="addr-block__tag">号码保护中</t-tag>
+            <view class="addr-block__virtual" bindtap="onVirtualNumberTip">
+              <t-tag size="small" variant="outline" theme="warning" class="addr-block__tag addr-block__tag--orange">
+                取件出示虚拟号
+              </t-tag>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <view class="goods-section">
+      <view class="goods-item" wx:for="{{goodsList}}" wx:key="id">
+        <t-image src="{{item.image}}" width="144rpx" height="144rpx" shape="round" mode="aspectFill" class="goods-item__img" />
+        <view class="goods-item__main">
+          <text class="goods-item__title">{{item.title}}</text>
+          <text class="goods-item__spec">{{item.spec}}</text>
+        </view>
+        <view class="goods-item__price-col">
+          <text class="goods-item__price">¥{{item.price}}</text>
+          <text class="goods-item__qty">x{{item.quantity}}</text>
+        </view>
+      </view>
+    </view>
+  </view>
+</scroll-view>

+ 6 - 0
miniprogram/module/order/pages/other-detail/other-detail.ts

@@ -77,6 +77,12 @@ Page({
         break;
     }
   },
+  // 查看物流详情
+  goLogistics() {
+    wx.navigateTo({
+      url: `/module/order/pages/logistics-detail/logistics-detail`,
+    });
+  },
   // 订单详情
   async load(id: string) {
     wx.showLoading({ title: "加载中" });

+ 1 - 1
miniprogram/module/order/pages/other-detail/other-detail.wxml

@@ -22,7 +22,7 @@
            <t-icon name="chevron-right" color="#000" size="40rpx" slot="note" />
         </view>
         <view>
-          <view class="goods-status {{goods.statusClass}}" wx:if="{{goods.statusText && orderStatus !== 'closed'}}">{{goods.statusText}}</view>
+          <view class="goods-status {{goods.statusClass}}" wx:if="{{goods.statusText && orderStatus !== 'closed'}}" bindtap="goLogistics">{{goods.statusText}}</view>
           <view class="goods-item">
             <image class="goods-image" src="{{goods.image}}" mode="aspectFill" wx:if="{{goods.image}}" />
             <view class="service-package-placeholder" wx:else>