Explorar el Código

feat(api): 添加订单地理位置和骑手配送相关接口类型定义

- 在 xsbOrderList 接口中添加 longitude 和 latitude 字段用于存储订单地理位置
- 新增 RiderInfo 接口定义骑手信息,包含骑手姓名、电话、经纬度及门店经纬度
- 新增 DeliveryNode 接口定义配送节点信息,包含内容、时间、订单号等字段
- 在 apiDefinitions 中注册 riderInfo 和 deliveryNode 两个新接口
- 在 globals.d.ts 中添加对应的 API 方法类型声明

feat(order): 实现订单详情页地图展示和配送节点追踪功能

- 在订单详情页添加地图组件,展示商家、用户和骑手位置
- 实现骑手信息和配送节点信息的获取和展示
- 添加订单状态对应的配送流程展示
- 实现配送节点弹窗展示订单追踪信息
- 优化订单详情页导航栏样式,使用自定义导航

refactor(cart): 调整购物车数量更新逻辑

- 移除首页和用户店铺中的购物车数量设置逻辑
- 优化购物车数据获取时机,提升页面性能

refactor(store): 移除用户登录时购物车数量设置

- 从用户登录逻辑中移除购物车数量设置相关代码

refactor(xsb): 优化商品分类页面购物车加载逻辑

- 调整购物车数据加载顺序,先获取购物车分类列表再加载购物车盒子

refactor(order): 修复订单列表刷新问题

- 移除订单页面 onShow 中的订单列表清空逻辑

chore(deps): 添加地图组件和粘性布局组件类型定义

- 在 auto-imports.d.ts 中添加 calculateCenterPointSpherical 工具函数
- 在 components.d.ts 中添加 wd-sticky 和 wd-sticky-box 组件类型

chore(utils): 新增球面中心点计算工具函数

- 实现 calculateCenterPointSpherical 函数,使用球面插值法计算两个经纬度的中心点
- 添加角度与弧度转换的辅助函数
zhangtao hace 4 días
padre
commit
d0eb212967

+ 59 - 0
src/api/api.type.d.ts

@@ -538,6 +538,14 @@ namespace Api {
     [property: string]: any
   }
   interface xsbOrderList {
+    /**
+     *  经度
+     */
+    longitude: number
+    /**
+     *  纬度
+     */
+    latitude: number
     /**
      * 实际总值(用户实付)
      */
@@ -1068,5 +1076,56 @@ namespace Api {
     typeCode: string
     value: string
   }
+  interface RiderInfo {
+    /**
+     * 骑手姓名
+     */
+    courierName?: string
+    /**
+     * 骑手电话
+     */
+    courierPhone?: string
+    /**
+     * 骑手纬度
+     */
+    riderLatitude?: number
+    /**
+     * 骑手经度
+     */
+    riderLongitude?: number
+    /**
+     * 门店纬度
+     */
+    shopLatitude?: number
+    /**
+     * 门店经度
+     */
+    shopLongitude?: number
+    [property: string]: any
+  }
+  interface DeliveryNode {
+    id: number
+    /**
+     * 内容
+     */
+    content?: string
+    /**
+     * 创建时间
+     */
+    createTime?: string
+    /**
+     * 订单号
+     */
+    orderNumber?: string
+    /**
+     * 订单备注
+     */
+    remark?: string
+    /**
+     * 修改时间
+     */
+    updateTime?: string
+    [property: string]: any
+  }
 
 }

+ 2 - 0
src/api/apiDefinitions.ts

@@ -54,6 +54,8 @@ export default {
   'xsb.confirmReceipt':['GET', '/smqjh-oms/api/v1/order/confirmReceipt'],
   'xsb.applyRefund':['POST', '/smqjh-oms/app-api/v1/refund/applyRefund'],
   'xsb.calculateMoney':['POST', '/smqjh-oms/app-api/v1/refund/calculateMoney'],
+  'xsb.riderInfo':['GET', '/smqjh-oms/api/v1/order/riderInfo'],
+  'xsb.deliveryNode':['GET', '/smqjh-oms/api/v1/order/deliveryNode'],
 
   'common.myShoppingCart':['GET', '/smqjh-oms/app-api/v1/shoppingCart/myShoppingCart'],
   'common.addShoppingCart':['POST', '/smqjh-oms/app-api/v1/shoppingCart/addShoppingCart'],

+ 18 - 0
src/api/globals.d.ts

@@ -192,6 +192,24 @@ declare global {
       ): Alova2Method<listData<Api.sysDict>, 'sys.dictPage', Config>;
     }
     xsb: {
+      deliveryNode<
+        Config extends Alova2MethodConfig<apiResData<Api.DeliveryNode[]>> & {
+          data: {
+            orderNumber: string;
+          };
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<Api.DeliveryNode[]>, 'xsb.deliveryNode', Config>;
+      riderInfo<
+        Config extends Alova2MethodConfig<apiResData<Api.RiderInfo>> & {
+          data: {
+            orderNumber: string
+          };
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<Api.RiderInfo>, 'xsb.riderInfo', Config>;
       calculateMoney<
         Config extends Alova2MethodConfig<apiResData<any>> & {
           data: {

+ 2 - 0
src/auto-imports.d.ts

@@ -16,6 +16,7 @@ declare global {
   const apiDefinitions: typeof import('./api/apiDefinitions')['default']
   const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
   const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
+  const calculateCenterPointSpherical: typeof import('./utils/index')['calculateCenterPointSpherical']
   const computed: typeof import('vue')['computed']
   const computedAsync: typeof import('@vueuse/core')['computedAsync']
   const computedEager: typeof import('@vueuse/core')['computedEager']
@@ -392,6 +393,7 @@ declare module 'vue' {
     readonly apiDefinitions: UnwrapRef<typeof import('./api/apiDefinitions')['default']>
     readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
     readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
+    readonly calculateCenterPointSpherical: UnwrapRef<typeof import('./utils/index')['calculateCenterPointSpherical']>
     readonly computed: UnwrapRef<typeof import('vue')['computed']>
     readonly computedAsync: UnwrapRef<typeof import('@vueuse/core')['computedAsync']>
     readonly computedEager: UnwrapRef<typeof import('@vueuse/core')['computedEager']>

+ 2 - 0
src/components.d.ts

@@ -40,6 +40,8 @@ declare module 'vue' {
     WdStatusTip: typeof import('wot-design-uni/components/wd-status-tip/wd-status-tip.vue')['default']
     WdStep: typeof import('wot-design-uni/components/wd-step/wd-step.vue')['default']
     WdSteps: typeof import('wot-design-uni/components/wd-steps/wd-steps.vue')['default']
+    WdSticky: typeof import('wot-design-uni/components/wd-sticky/wd-sticky.vue')['default']
+    WdStickyBox: typeof import('wot-design-uni/components/wd-sticky-box/wd-sticky-box.vue')['default']
     WdSwiper: typeof import('wot-design-uni/components/wd-swiper/wd-swiper.vue')['default']
     WdTab: typeof import('wot-design-uni/components/wd-tab/wd-tab.vue')['default']
     WdTabbar: typeof import('wot-design-uni/components/wd-tabbar/wd-tabbar.vue')['default']

+ 2 - 0
src/composables/useTabbar.ts

@@ -35,6 +35,8 @@ export function useTabbar() {
   }
 
   const setTabbarItem = (name: string, value: number) => {
+    console.log('设置市民请集合购物车数量===========================')
+
     const tabbarItem = tabbarItems.value.find(item => item.name === name)
     if (tabbarItem) {
       tabbarItem.value = value

+ 2 - 1
src/pages.json

@@ -146,7 +146,8 @@
           "name": "xsb-orderDetaile",
           "islogin": true,
           "style": {
-            "navigationBarTitleText": "订单详情"
+            "navigationBarTitleText": "订单详情",
+            "navigationStyle": "custom"
           }
         },
         {

+ 1 - 2
src/pages/index/index.vue

@@ -19,7 +19,6 @@ const addressStore = useAddressStore()
 const { statusBarHeight, MenuButtonHeight, opcity, isOnlineAudit } = storeToRefs(useSysStore())
 const { name } = storeToRefs(addressStore)
 const { userInfo } = storeToRefs(useUserStore())
-const { getTotalNum } = storeToRefs(useSmqjhCartStore())
 const xsbStore = ref<typeof import('@/subPack-xsb/store-xsb/sys')>()
 const { data: goodsList, isLastPage, page, reload, error, refresh } = usePagination((pageNum, pageSize) =>
   Apis.xsb.getSearchProductList({ data: { pageNum, pageSize, salesNum: 'DESC', shopId: xsbStore.value?.useSysXsbStore().SelectShopInfo.shopId || 2, channelId: userInfo.value.channelId || 1 } }), {
@@ -38,7 +37,7 @@ const loading = ref(true)
 onShow(async () => {
   useSysStore().getAudit()
   useSmqjhCartStore().getCartList('XSB')
-  useTabbar().setTabbarItem('smqjh-cart', getTotalNum.value)
+
   xsbStore.value = await AsyncImport('@/subPack-xsb/store-xsb/sys')
   refresh()
 })

+ 0 - 2
src/store/user.ts

@@ -55,8 +55,6 @@ export const useUserStore = defineStore('user', {
         await this.getuserAddresslist()
         this.getSelectedAddress()
         await useSmqjhCartStore().getCartList('XSB')
-        const { getTotalNum } = storeToRefs(useSmqjhCartStore())
-        useTabbar().setTabbarItem('smqjh-cart', getTotalNum.value)
       }
     },
     async updataUserInfo(data: Api.userInfo) {

+ 3 - 5
src/subPack-xsb/commonTab/components/classfiy.vue

@@ -218,12 +218,10 @@ onMounted(async () => {
       topScrollView.value = topNavActive.value
     })
   }
+  getCartBox()
   if (token.value) {
-    goodsLoading.value = 'loading'
+    // goodsLoading.value = 'loading'
     await getCartCategorList()
-    if (cartList.value.length) {
-      getCartBox()
-    }
   }
 })
 
@@ -651,7 +649,7 @@ function handlePay() {
       <template #footer>
         <view class="box-border w-full flex items-center justify-between py20rpx">
           <view class="w-48%">
-            <wd-button plain hairline block @click="selectGoods = false">
+            <wd-button hairline plain block @click="selectGoods = false">
               取消
             </wd-button>
           </view>

+ 0 - 1
src/subPack-xsb/order/index.vue

@@ -51,7 +51,6 @@ onReachBottom(() => {
   }
 })
 onShow(() => {
-  orderList.value = []
   refresh()
 })
 

+ 399 - 205
src/subPack-xsb/orderDetaile/index.vue

@@ -1,19 +1,58 @@
 <script setup lang="ts">
+import type { MapMarker, MapPolyline } from '@uni-helper/uni-types'
 import { OrderStatus, handleCommonCancelOrder, handleCommonDeleteOrder, handleCommonOrderStatusText } from '../utils/order-data'
 import { StaticUrl } from '@/config'
 import router from '@/router'
 import { getWxCommonPayment, handleCommonPayMent } from '@/subPack-xsb/utils/confirm-order'
 
+const { statusBarHeight, MenuButtonHeight } = storeToRefs(useSysStore())
+const { userInfo } = storeToRefs(useUserStore())
 definePage({
   name: 'xsb-orderDetaile',
   islogin: true,
   style: {
     navigationBarTitleText: '订单详情',
+    navigationStyle: 'custom',
   },
 })
 const collapse = ref(false)
 const orderInfo = ref<Api.xsbOrderList>()
+const orderNum = ref()
+const mapInfo = ref<Api.RiderInfo>()
+const mapMarkers = ref<MapMarker[]>([])
+const mapPolyLine = ref<MapPolyline[]>([])
+const showNode = ref(false)
+const NodeList = ref<Api.DeliveryNode[]>([])
+const showMapStatus = [OrderStatus.OrderAccepted, OrderStatus.OrderWaitDelivery, OrderStatus.OrderDelivering, OrderStatus.OrderArrived]
+const mapStaticShopImg = {
+  [OrderStatus.OrderAccepted]: `${StaticUrl}/order-map-jian.png`,
+  [OrderStatus.OrderWaitDelivery]: `${StaticUrl}/order-map-wait.png`,
+  [OrderStatus.OrderDelivering]: `${StaticUrl}/order-map-qishou.png`,
+  [OrderStatus.OrderArrived]: `${StaticUrl}/order-map-qishou.png`,
+}
+const mapTextShop = {
+  [OrderStatus.OrderAccepted]: '商家拣货中',
+  [OrderStatus.OrderWaitDelivery]: '等待骑手配送',
+  [OrderStatus.OrderDelivering]: '骑手配送中',
+  [OrderStatus.OrderArrived]: '已送达',
+}
+
+const mapLation = computed(() => {
+  const lation = {
+    latitude: mapInfo.value?.shopLatitude,
+    longitude: mapInfo.value?.shopLongitude,
+    scale: 16,
+  }
+  if (orderInfo.value && mapInfo.value) {
+    const lations = calculateCenterPointSpherical({ lat: orderInfo.value.latitude, lng: orderInfo.value.longitude }, { lat: mapInfo.value.shopLatitude as number, lng: mapInfo.value.shopLongitude as number })
+    lation.latitude = lations.lat
+    lation.longitude = lations.lng
+    lation.scale = 6
+  }
+  return lation
+})
 onLoad((options: any) => {
+  orderNum.value = options.id
   getDetail(options.id)
 })
 async function getDetail(id: string) {
@@ -23,6 +62,10 @@ async function getDetail(id: string) {
     },
   })
   orderInfo.value = data
+  if (showMapStatus.includes(data.hbOrderStatus)) {
+    getOrderMapInf()
+  }
+  getOrderNode()
 }
 
 function handleCollapse() {
@@ -66,112 +109,245 @@ function handleAfterSale() {
   }
   router.push({ name: 'common-afterSales', params: { order: JSON.stringify(orderInfo.value?.orderItemList) } })
 }
+async function getOrderMapInf() {
+  const res = await Apis.xsb.riderInfo({ data: { orderNumber: orderNum.value } })
+  mapInfo.value = res.data
+  mapMarkers.value.push({
+    id: 1,
+    latitude: Number(mapInfo.value?.shopLatitude),
+    longitude: Number(mapInfo.value?.shopLongitude),
+    iconPath: mapStaticShopImg[orderInfo.value?.hbOrderStatus as keyof typeof mapStaticShopImg],
+    width: 70,
+    height: 80,
+    label: {
+      content: mapTextShop[orderInfo.value?.hbOrderStatus as keyof typeof mapTextShop],
+      color: '#333',
+      fontSize: 14,
+      x: 0,
+      y: 0,
+      anchorX: 40,
+      anchorY: -120,
+      borderRadius: 8,
+      borderWidth: 0,
+      borderColor: '',
+      bgColor: '#fff',
+      padding: 5,
+      display: 'BYCLICK',
+      textAlign: 'left',
+      customCallout: {
+        display: 'BYCLICK',
+        anchorX: 0,
+        anchorY: 0,
+      },
+      ariaLabel: '',
+      joinCluster: false,
+    },
+  }, {
+    id: 2,
+    longitude: Number(orderInfo.value?.longitude),
+    latitude: Number(orderInfo.value?.latitude),
+    iconPath: userInfo.value.avatarUrl || `${StaticUrl}/9.png`,
+    width: 50,
+    height: 50,
+  })
+
+  if (mapInfo.value?.riderLatitude && mapInfo.value?.riderLongitude) {
+    mapMarkers.value.push({
+      id: 3,
+      longitude: Number(mapInfo.value?.riderLongitude),
+      latitude: Number(mapInfo.value?.riderLatitude),
+      iconPath: `${StaticUrl}/order-map-qishou.png`,
+      width: 70,
+      height: 80,
+      label: {
+        content: '骑手正在送货',
+        color: '#333',
+        fontSize: 14,
+        x: 0,
+        y: 0,
+        anchorX: 40,
+        anchorY: -120,
+        borderRadius: 8,
+        borderWidth: 0,
+        borderColor: '',
+        bgColor: '#fff',
+        padding: 5,
+        display: 'BYCLICK',
+        textAlign: 'left',
+        customCallout: {
+          display: 'BYCLICK',
+          anchorX: 0,
+          anchorY: 0,
+        },
+        ariaLabel: '',
+        joinCluster: false,
+      },
+    })
+    mapPolyLine.value.push({
+      points: [
+        {
+          longitude: Number(mapInfo.value?.riderLongitude),
+          latitude: Number(mapInfo.value?.riderLatitude),
+        },
+        {
+          longitude: Number(orderInfo.value?.longitude),
+          latitude: Number(orderInfo.value?.latitude),
+        },
+      ],
+      color: '#9ED605',
+      width: 3,
+    })
+    // 绘制路径骑手和店家只能存在一个
+    return
+  }
+  else {
+    mapPolyLine.value.push({
+      points: [
+        {
+          longitude: Number(orderInfo.value?.longitude),
+          latitude: Number(orderInfo.value?.latitude),
+        },
+        {
+          longitude: Number(mapInfo.value?.shopLongitude),
+          latitude: Number(mapInfo.value?.shopLatitude),
+        },
+      ],
+      color: '#9ED605',
+      width: 3,
+    })
+  }
+
+  console.log(mapPolyLine.value, 'mapPolyLine')
+}
+async function getOrderNode() {
+  const res = await Apis.xsb.deliveryNode({
+    data: {
+      orderNumber: String(orderInfo.value?.orderNumber),
+    },
+  })
+  NodeList.value = res.data
+}
+
+function handleMarkerTap(e: UniHelper.MapOnMarkertapEvent) {
+  console.log(e, 'sadada')
+  showNode.value = true
+}
 </script>
 
 <template>
-  <view v-if="orderInfo" class="page-xsb px24rpx">
-    <view class="pt20rpx">
-      <view class="text-36rpx font-semibold">
-        <view v-if="orderInfo.hbOrderStatus === OrderStatus.PaddingPay">
-          <view class="flex items-center">
-            请在
-            <wd-count-down format="mm:ss" :time="handleCommonOrderStatusText(orderInfo)" @finish="handleFinish">
-              <template #default="{ current }">
-                <view class="flex items-center">
-                  <view> {{ current.minutes }} 分</view>
-                  <view> {{ current.seconds }} 秒</view>
-                </view>
-              </template>
-            </wd-count-down>
-            内支付
-          </view>
-          <view class="mt20rpx text-28rpx text-#AAAAAA">
-            现在支付:预计10:40-10:55送达
-          </view>
-          <view class="btn mt20rpx flex items-center justify-between">
-            <view class="info-btn mr20rpx w226rpx">
-              <wd-button type="info" @click="handleCancel">
-                取消订单
-              </wd-button>
+  <view v-if="orderInfo" class="page-xsb" :class="[collapse ? 'pt850rpx' : ``]" :style="{ paddingTop: `${collapse ? '850rpx' : (`${Number(statusBarHeight) + Number(MenuButtonHeight) + 10}px`)}` }">
+    <wd-navbar
+      title="订单详情" :bordered="false" :z-index="99"
+      safe-area-inset-top left-arrow fixed
+      @click-left="router.back()"
+    />
+    <view v-if="showMapStatus.includes(orderInfo?.hbOrderStatus)">
+      <map id="orderMap" :polyline="mapPolyLine" :scale="mapLation.scale" :markers="mapMarkers" :latitude="mapLation.latitude" :longitude="mapLation.longitude" class="fixed top-0 z80 h-900rpx w-screen" @markertap="handleMarkerTap" />
+    </view>
+
+    <view class="relative z-90 box-border bg-#f6f6f6 px24rpx">
+      <view class="pt20rpx">
+        <view class="text-36rpx font-semibold">
+          <view v-if="orderInfo.hbOrderStatus === OrderStatus.PaddingPay">
+            <view class="flex items-center">
+              请在
+              <wd-count-down format="mm:ss" :time="handleCommonOrderStatusText(orderInfo)" @finish="handleFinish">
+                <template #default="{ current }">
+                  <view class="flex items-center">
+                    <view> {{ current.minutes }} 分</view>
+                    <view> {{ current.seconds }} 秒</view>
+                  </view>
+                </template>
+              </wd-count-down>
+              内支付
             </view>
-            <view class="ml20rpx flex-1">
-              <wd-button @click="handlePay">
-                立即支付¥{{ orderInfo.actualTotal }}
-              </wd-button>
+            <view class="mt20rpx text-28rpx text-#AAAAAA">
+              现在支付:预计10:40-10:55送达
+            </view>
+            <view class="btn mt20rpx flex items-center justify-between">
+              <view class="info-btn mr20rpx w226rpx">
+                <wd-button type="info" @click="handleCancel">
+                  取消订单
+                </wd-button>
+              </view>
+              <view class="ml20rpx flex-1">
+                <wd-button @click="handlePay">
+                  立即支付¥{{ orderInfo.actualTotal }}
+                </wd-button>
+              </view>
             </view>
           </view>
-        </view>
-        <view v-if="orderInfo.hbOrderStatus !== OrderStatus.PaddingPay" class="flex items-center" @click="handleCollapse">
-          <view v-if="orderInfo.hbOrderStatus === OrderStatus.OrderAccepted" class="mr10rpx">
-            商家拣货中
-          </view>
-          <view v-if="orderInfo.hbOrderStatus === OrderStatus.OrderWaitDelivery" class="mr10rpx">
-            订单待配送
-          </view>
-          <view v-if="orderInfo.hbOrderStatus === OrderStatus.OrderDelivering" class="mr10rpx">
-            订单配送中
-          </view>
-          <view v-if="orderInfo.hbOrderStatus === OrderStatus.OrderCancelAudit" class="mr10rpx">
-            订单取消审核
-          </view>
-          <view v-if="orderInfo.hbOrderStatus === OrderStatus.OrderCancel" class="mr10rpx">
-            订单取消
-          </view>
-          <view v-if="orderInfo.hbOrderStatus === OrderStatus.OrderArrived" class="mr10rpx">
-            订单已送达
-          </view>
-          <view v-if="orderInfo.hbOrderStatus === OrderStatus.OrderCompleted" class="mr10rpx">
-            订单已完成
+          <view v-if="orderInfo.hbOrderStatus !== OrderStatus.PaddingPay" class="flex items-center" @click="handleCollapse">
+            <view v-if="orderInfo.hbOrderStatus === OrderStatus.OrderAccepted" class="mr10rpx">
+              商家拣货中
+            </view>
+            <view v-if="orderInfo.hbOrderStatus === OrderStatus.OrderWaitDelivery" class="mr10rpx">
+              订单待配送
+            </view>
+            <view v-if="orderInfo.hbOrderStatus === OrderStatus.OrderDelivering" class="mr10rpx">
+              订单配送中
+            </view>
+            <view v-if="orderInfo.hbOrderStatus === OrderStatus.OrderCancelAudit" class="mr10rpx">
+              订单取消审核
+            </view>
+            <view v-if="orderInfo.hbOrderStatus === OrderStatus.OrderCancel" class="mr10rpx">
+              订单取消
+            </view>
+            <view v-if="orderInfo.hbOrderStatus === OrderStatus.OrderArrived" class="mr10rpx">
+              订单已送达
+            </view>
+            <view v-if="orderInfo.hbOrderStatus === OrderStatus.OrderCompleted" class="mr10rpx">
+              订单已完成
+            </view>
+            <view :class="[collapse ? 'rotate-90' : '']">
+              <wd-icon name="arrow-right" size="22px" />
+            </view>
           </view>
-          <view :class="[collapse ? 'rotate-90' : '']">
-            <wd-icon name="arrow-right" size="22px" />
+          <view v-if="orderInfo.hbOrderStatus === OrderStatus.OrderCancel" class="mt20rpx text-28rpx text-#AAAAAA">
+            取消原因:{{ orderInfo.cancelReason }}
           </view>
         </view>
-        <view v-if="orderInfo.hbOrderStatus === OrderStatus.OrderCancel" class="mt20rpx text-28rpx text-#AAAAAA">
-          取消原因:{{ orderInfo.cancelReason }}
-        </view>
-      </view>
-    </view>
-    <view class="mt20rpx rounded-16rpx bg-white p24rpx">
-      <view v-if="orderInfo.dvyFlowId" class="mb24rpx text-24rpx">
-        <view class="mb20rpx">
-          快递单号
-        </view>
-        <view class="text-48rpx font-semibold">
-          {{ orderInfo.dvyFlowId }}
-        </view>
       </view>
-      <view class="grid grid-cols-5 text-28rpx text-#222">
-        <view v-if="[OrderStatus.OrderCancel, OrderStatus.OrderCompleted].includes(Number(orderInfo.hbOrderStatus))" class="flex flex-col items-center" @click="handelDel">
-          <image
-            :src="`${StaticUrl}/orderDetaile-del.png`"
-            class="h40rpx w40rpx"
-          />
-          <view class="mt40rpx">
-            删除订单
+      <view class="mt20rpx rounded-16rpx bg-white p24rpx">
+        <view v-if="orderInfo.dvyFlowId" class="mb24rpx text-24rpx">
+          <view class="mb20rpx">
+            快递单号
+          </view>
+          <view class="text-48rpx font-semibold">
+            {{ orderInfo.dvyFlowId }}
           </view>
         </view>
-        <view class="contact relative flex flex-col items-center">
-          <image
-            :src="`${StaticUrl}/orderDetaile-wx.png`"
-            class="h40rpx w40rpx"
-          />
-          <button open-type="contact" class="zbutton mt40rpx">
-            <view class="text-28rpx">
-              联系商家
+        <view class="grid grid-cols-5 text-28rpx text-#222">
+          <view v-if="[OrderStatus.OrderCancel, OrderStatus.OrderCompleted].includes(Number(orderInfo.hbOrderStatus))" class="flex flex-col items-center" @click="handelDel">
+            <image
+              :src="`${StaticUrl}/orderDetaile-del.png`"
+              class="h40rpx w40rpx"
+            />
+            <view class="mt40rpx">
+              删除订单
             </view>
-          </button>
-        </view>
-        <view v-if="[OrderStatus.OrderCompleted, OrderStatus.OrderWaitDelivery, OrderStatus.OrderAccepted].includes(orderInfo.hbOrderStatus) " class="flex flex-col items-center" @click="handleAfterSale">
-          <image
-            :src="`${StaticUrl}/orderDetaile-shou.png`"
-            class="h40rpx w40rpx"
-          />
-          <view class="mt40rpx">
-            申请售后
           </view>
-        </view>
-        <!-- <view class="flex  flex-col items-center " @click="router.push({ name: 'common-revalue' })">
+          <view class="contact relative flex flex-col items-center">
+            <image
+              :src="`${StaticUrl}/orderDetaile-wx.png`"
+              class="h40rpx w40rpx"
+            />
+            <button open-type="contact" class="zbutton mt40rpx">
+              <view class="text-28rpx">
+                联系商家
+              </view>
+            </button>
+          </view>
+          <view v-if="[OrderStatus.OrderCompleted, OrderStatus.OrderWaitDelivery, OrderStatus.OrderAccepted].includes(orderInfo.hbOrderStatus) " class="flex flex-col items-center" @click="handleAfterSale">
+            <image
+              :src="`${StaticUrl}/orderDetaile-shou.png`"
+              class="h40rpx w40rpx"
+            />
+            <view class="mt40rpx">
+              申请售后
+            </view>
+          </view>
+          <!-- <view class="flex  flex-col items-center " @click="router.push({ name: 'common-revalue' })">
           <image
             :src="`${StaticUrl}/orderDetaile-pj.png`"
             class="h40rpx w40rpx"
@@ -180,7 +356,7 @@ function handleAfterSale() {
             评价晒单
           </view>
         </view> -->
-        <!-- <view class="flex  flex-col items-center">
+          <!-- <view class="flex  flex-col items-center">
           <image
             :src="`${StaticUrl}/orderDetaile-bao.png`"
             class="h40rpx w40rpx"
@@ -189,143 +365,161 @@ function handleAfterSale() {
             再次购买
           </view>
         </view> -->
-      </view>
-    </view>
-    <view class="mt20rpx rounded-16rpx bg-white p24rpx">
-      <view class="flex items-center">
-        <image
-          :src="`${StaticUrl}/orderDetaile-user.png`"
-          class="mr20rpx h40rpx w40rpx"
-        />
-        <view class="text-32rpx text-#222 font-semibold">
-          {{ orderInfo?.consigneeName }} {{ orderInfo?.consigneeMobile }}
         </view>
       </view>
-      <view class="mt20rpx text-28rpx text-#AAAAAA">
-        {{ orderInfo?.consigneeAddress }}
-      </view>
-    </view>
-    <view class="mt20rpx rounded-16rpx bg-white p24rpx">
-      <view class="flex items-center">
-        <image
-          :src="`${StaticUrl}/order-icon.png`"
-          class="h36rpx w36rpx"
-        />
-        <view class="ml20rpx text-32rpx font-semibold">
-          {{ orderInfo?.shopName }}
+      <view class="mt20rpx rounded-16rpx bg-white p24rpx">
+        <view class="flex items-center">
+          <image
+            :src="`${StaticUrl}/orderDetaile-user.png`"
+            class="mr20rpx h40rpx w40rpx"
+          />
+          <view class="text-32rpx text-#222 font-semibold">
+            {{ orderInfo?.consigneeName }} {{ orderInfo?.consigneeMobile }}
+          </view>
+        </view>
+        <view class="mt20rpx text-28rpx text-#AAAAAA">
+          {{ orderInfo?.consigneeAddress }}
         </view>
       </view>
-      <view class="my24rpx h2rpx w-full bg-#F0F0F0" />
-      <CollapsePanel :line-height="150">
-        <view v-for="item in orderInfo?.orderItemList" :key="item.skuId" class="mb20rpx w-full flex items-center">
-          <view class="mr20rpx w120rpx flex-shrink-0">
-            <image
-              :src="item.pic"
-              class="h120rpx w120rpx"
-            />
+      <view class="mt20rpx rounded-16rpx bg-white p24rpx">
+        <view class="flex items-center">
+          <image
+            :src="`${StaticUrl}/order-icon.png`"
+            class="h36rpx w36rpx"
+          />
+          <view class="ml20rpx text-32rpx font-semibold">
+            {{ orderInfo?.shopName }}
           </view>
-          <view class="flex-1">
-            <view class="w-full flex items-center justify-between font-semibold">
-              <view class="text-28rpx">
-                {{ item.skuName }}
+        </view>
+        <view class="my24rpx h2rpx w-full bg-#F0F0F0" />
+        <CollapsePanel :line-height="150">
+          <view v-for="item in orderInfo?.orderItemList" :key="item.skuId" class="mb20rpx w-full flex items-center">
+            <view class="mr20rpx w120rpx flex-shrink-0">
+              <image
+                :src="item.pic"
+                class="h120rpx w120rpx"
+              />
+            </view>
+            <view class="flex-1">
+              <view class="w-full flex items-center justify-between font-semibold">
+                <view class="text-28rpx">
+                  {{ item.skuName }}
+                </view>
+                <view class="text-32rpx text-#FF4D3A">
+                  ¥{{ item.price }}
+                </view>
               </view>
-              <view class="text-32rpx text-#FF4D3A">
-                ¥{{ item.price }}
+              <view class="text-24rpx text-#AAAAAA">
+                规格:{{ item.spec }}
+              </view>
+              <view class="text-24rpx text-#AAAAAA">
+                ×{{ item.prodCount }}
               </view>
-            </view>
-            <view class="text-24rpx text-#AAAAAA">
-              规格:{{ item.spec }}
-            </view>
-            <view class="text-24rpx text-#AAAAAA">
-              ×{{ item.prodCount }}
             </view>
           </view>
-        </view>
-      </CollapsePanel>
+        </CollapsePanel>
 
-      <view class="mt24rpx h2rpx w-full bg-#F0F0F0" />
-      <view class="mt24rpx flex items-center justify-between">
-        <view class="text-28rpx">
-          商品金额
-        </view>
-        <view class="text-#FF4A39 font-semibold">
-          ¥{{ orderInfo?.total }}
-        </view>
-      </view>
-      <view class="mt24rpx flex items-center justify-between">
-        <view v-if="orderInfo.dvyType == 3">
-          配送费(即时配送)
-        </view>
-        <view v-if="orderInfo.dvyType == 1">
-          快递
-        </view>
-        <view class="text-#FF4A39 font-semibold">
-          ¥{{ orderInfo?.freightAmount }}
-        </view>
-      </view>
-      <view class="mt24rpx flex items-center justify-between">
-        <view class="text-28rpx">
-          积分
-        </view>
-        <view class="text-#FF4A39 font-semibold">
-          -¥{{ Number(orderInfo?.offsetPoints) / 100 }}
-        </view>
-      </view>
-      <view class="my24rpx h2rpx w-full bg-#F0F0F0" />
-      <view class="flex items-center justify-end">
-        <view class="text-28rpx">
-          总计{{ orderInfo.orderMoney }} 共减 {{ Number(orderInfo.offsetPoints) / 100 }}
-        </view>
-        <view class="ml20rpx text-28rpx text-#FF4D3A font-semibold">
-          {{ [OrderStatus.PaddingPay, OrderStatus.OrderCancel].includes(Number(orderInfo.hbOrderStatus)) ? '需' : '实' }}付款¥{{ orderInfo.actualTotal }}
+        <view class="mt24rpx h2rpx w-full bg-#F0F0F0" />
+        <view class="mt24rpx flex items-center justify-between">
+          <view class="text-28rpx">
+            商品金额
+          </view>
+          <view class="text-#FF4A39 font-semibold">
+            ¥{{ orderInfo?.total }}
+          </view>
         </view>
-      </view>
-    </view>
-    <view class="mt20rpx rounded-16rpx bg-white p24rpx">
-      <view class="mb24rpx text-28rpx font-semibold">
-        订单信息
-      </view>
-      <view class="pb20rpx">
-        <view class="mb28rpx flex items-center justify-between">
-          <view class="text-28rpx text-#AAAAAA">
-            订单编号
+        <view class="mt24rpx flex items-center justify-between">
+          <view v-if="orderInfo.dvyType == 3">
+            配送费(即时配送)
           </view>
-          <view class="flex items-center">
-            <text class="text-#222">
-              {{ orderInfo?.orderNumber }}
-            </text>
-            <view class="ml10rpx" @click="handleCopy">
-              <wd-icon name="file-copy" size="22px" />
-            </view>
+          <view v-if="orderInfo.dvyType == 1">
+            快递
+          </view>
+          <view class="text-#FF4A39 font-semibold">
+            ¥{{ orderInfo?.freightAmount }}
           </view>
         </view>
-        <view class="mb28rpx flex items-center justify-between">
-          <view class="text-28rpx text-#AAAAAA">
-            支付方式
+        <view class="mt24rpx flex items-center justify-between">
+          <view class="text-28rpx">
+            积分
           </view>
-          <view class="text-#222">
-            微信支付
+          <view class="text-#FF4A39 font-semibold">
+            -¥{{ Number(orderInfo?.offsetPoints) / 100 }}
           </view>
         </view>
-        <view class="mb28rpx flex items-center justify-between">
-          <view class="text-28rpx text-#AAAAAA">
-            下单时间
+        <view class="my24rpx h2rpx w-full bg-#F0F0F0" />
+        <view class="flex items-center justify-end">
+          <view class="text-28rpx">
+            总计{{ orderInfo.orderMoney }} 共减 {{ Number(orderInfo.offsetPoints) / 100 }}
           </view>
-          <view class="text-#222">
-            {{ orderInfo?.createTime }}
+          <view class="ml20rpx text-28rpx text-#FF4D3A font-semibold">
+            {{ [OrderStatus.PaddingPay, OrderStatus.OrderCancel].includes(Number(orderInfo.hbOrderStatus)) ? '需' : '实' }}付款¥{{ orderInfo.actualTotal }}
           </view>
         </view>
-        <view class="mb28rpx flex items-center justify-between">
-          <view class="text-28rpx text-#AAAAAA">
-            备注信息
+      </view>
+      <view class="mt20rpx rounded-16rpx bg-white p24rpx">
+        <view class="mb24rpx text-28rpx font-semibold">
+          订单信息
+        </view>
+        <view class="pb20rpx">
+          <view class="mb28rpx flex items-center justify-between">
+            <view class="text-28rpx text-#AAAAAA">
+              订单编号
+            </view>
+            <view class="flex items-center">
+              <text class="text-#222">
+                {{ orderInfo?.orderNumber }}
+              </text>
+              <view class="ml10rpx" @click="handleCopy">
+                <wd-icon name="file-copy" size="22px" />
+              </view>
+            </view>
+          </view>
+          <view class="mb28rpx flex items-center justify-between">
+            <view class="text-28rpx text-#AAAAAA">
+              支付方式
+            </view>
+            <view class="text-#222">
+              微信支付
+            </view>
+          </view>
+          <view class="mb28rpx flex items-center justify-between">
+            <view class="text-28rpx text-#AAAAAA">
+              下单时间
+            </view>
+            <view class="text-#222">
+              {{ orderInfo?.createTime }}
+            </view>
           </view>
-          <view class="text-#222">
-            {{ orderInfo?.remarks || '无' }}
+          <view class="mb28rpx flex items-center justify-between">
+            <view class="text-28rpx text-#AAAAAA">
+              备注信息
+            </view>
+            <view class="text-#222">
+              {{ orderInfo?.remarks || '无' }}
+            </view>
           </view>
         </view>
       </view>
+      <view class="h80rpx" />
     </view>
-    <view class="h30rpx" />
+    <Zpopup v-model="showNode" :showfooter="false">
+      <view class="p24rpx">
+        <view class="text-center text-32rpx font-semibold">
+          订单追踪
+        </view>
+        <wd-steps :active="0" vertical dot>
+          <wd-step v-for="item in NodeList" :key="item.id">
+            <template #title>
+              {{ item.content }}
+            </template>
+            <template #description>
+              {{ item.createTime }}
+            </template>
+          </wd-step>
+        </wd-steps>
+      </view>
+    </Zpopup>
   </view>
 </template>
 

+ 52 - 0
src/utils/index.ts

@@ -23,3 +23,55 @@ export function isWxMiniProgram() {
     resolve(1)
   })
 }
+/**
+ * 将角度转换为弧度
+ */
+function toRadians(degrees: number): number {
+  return degrees * Math.PI / 180
+}
+
+/**
+ * 将弧度转换为角度
+ */
+function toDegrees(radians: number): number {
+  return radians * 180 / Math.PI
+}
+
+/**
+ * 计算两个经纬度的中心点(球面插值法)
+ * 适用于任意距离,考虑地球曲率,更精确
+ *
+ * @param pointA 第一个经纬度坐标
+ * @param pointB 第二个经纬度坐标
+ * @returns 中心点经纬度坐标
+ */
+export function calculateCenterPointSpherical(
+  pointA: { lat: number, lng: number },
+  pointB: { lat: number, lng: number },
+): { lat: number, lng: number } {
+  // 将经纬度转换为弧度
+  const lat1 = toRadians(pointA.lat)
+  const lng1 = toRadians(pointA.lng)
+  const lat2 = toRadians(pointB.lat)
+  const lng2 = toRadians(pointB.lng)
+
+  // 计算纬度的差值
+  const dLng = lng2 - lng1
+
+  // 使用球面插值公式计算中心点
+  const Bx = Math.cos(lat2) * Math.cos(dLng)
+  const By = Math.cos(lat2) * Math.sin(dLng)
+
+  const centerLat = Math.atan2(
+    Math.sin(lat1) + Math.sin(lat2),
+    Math.sqrt((Math.cos(lat1) + Bx) * (Math.cos(lat1) + Bx) + By * By),
+  )
+
+  const centerLng = lng1 + Math.atan2(By, Math.cos(lat1) + Bx)
+
+  // 将结果转换回角度
+  return {
+    lat: toDegrees(centerLat),
+    lng: toDegrees(centerLng),
+  }
+}