Selaa lähdekoodia

feat(order): 新增退款订单列表及物流跟踪功能

- 在订单类型定义中新增 refundOrderList 字段支持退款订单数据
- 优化开发环境配置状态,切换至指定 IP 地址
- 首页导航“景区”按钮显示规则调整,兼容线上审核状态
- 添加“加油”横幅点击事件,跳转至微信公众号文章
- 订单列表页增加缓存机制,提升标签切换时的数据响应速度
- 订单列表页支持根据状态参数加载对应订单数据
- 退款售后页面tab新增状态字段,跳转时携带状态参数
- 用户订单详情页面新增退款订单信息展示,支持多状态显示
- 物流功能支持多种快递类型,点击可查看物流节点或调用快递插件
- 在订单组件增加“查看物流”按钮,条件显示根据订单状态判断
- 适配新版快递类型,支持快递类型为1和10的物流追踪按钮展示
- 优化个人中心订单tab跳转逻辑,支持状态参数传递
- 调整订单详情页拖运展示逻辑,集成微信官方快递小程序组件
- 修复manifest.json多余右大括号问题,保持文件格式正确
- 删除无效空行及多余代码片段,优化代码风格和结构
zhangtao 5 päivää sitten
vanhempi
commit
6acd69ee61

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

@@ -741,6 +741,33 @@ namespace Api {
      */
     djkOrderAttachInfo?: OmsDjkOrderAttach
     omsOrderOilVO?: OmsOrderOilVO
+    refundOrderList?: {
+      /**
+       * 申请时间
+       */
+      createTime?: string
+      /**
+       * 退款金额(元)
+       */
+      refundMoney?: number
+      /**
+       * 退款订单编号
+       */
+      refundNumber?: string
+      /**
+       * 退款总订单状态 0-进行中,1-已完成,2-积分退款成功微信退款失败,3-已取消,4-驳回
+       */
+      refundOrderStatus?: number
+      /**
+       * 退款状态描述
+       */
+      refundOrderStatusDesc?: string
+      /**
+       * 用户实际退款金额(元)
+       */
+      userRefundMoney?: number
+      [property: string]: any
+    }[]
 
   }
   interface OmsOrderOilVO {

+ 3 - 3
src/config/index.ts

@@ -8,12 +8,12 @@ const mapEnvVersion = {
   // develop: 'http://192.168.1.253:8080',
   // develop: 'http://192.168.0.19:8080', // 邓
   // develop: 'http://192.168.0.217:8080', // 黄
-  // develop: 'http://192.168.0.11:8080', // 王
+  develop: 'http://192.168.0.11:8080', // 王
   // develop: 'http://192.168.1.89:8080', // 田
   // develop: 'http://74949mkfh190.vicp.fun', // 付
-  develop: 'http://47.109.84.152:8081',
+  // develop: 'http://47.109.84.152:8081',
   // develop: 'https://5ed0f7cc.r9.vip.cpolar.cn',
-  develop: 'https://smqjh.api.zswlgz.com',
+  // develop: 'https://smqjh.api.zswlgz.com',
   /**
    * 体验版
    */

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

@@ -61,7 +61,7 @@ const navList = computed(() => {
     { icon: `${StaticUrl}/smqjh-vip.png`, title: '视频权益', name: 'video-rights-tabbar', show: !isOnlineAudit.value },
     { icon: `${StaticUrl}/smqjh-djk.png`, title: '大健康', name: 'djk-homeTabbar', show: true },
     { icon: `${StaticUrl}/smqjh-jiayou.png`, title: '加油', name: 'refueling-tabbar', show: true }, // refueling-tabbar
-    { icon: `${StaticUrl}/smqjh-attractions.png`, title: '景区', name: 'attractions-tabbar', show: true },
+    { icon: `${StaticUrl}/smqjh-attractions.png`, title: '景区', name: '', show: !isOnlineAudit.value },
     { icon: `${StaticUrl}/smqjh-diancan.png`, title: '大牌点餐', name: '', show: !isOnlineAudit.value },
     { icon: `${StaticUrl}/smqjh-jiudian.png`, title: '酒店民宿', name: '', show: !isOnlineAudit.value },
     { icon: `${StaticUrl}/smqjh-daijia.png`, title: '代驾', name: '', show: !isOnlineAudit.value },
@@ -90,6 +90,7 @@ function handleClick(name: string) {
     ;(wx as any).openOfficialAccountProfile({
       username: 'gh_6a682fa2ed1d',
     })
+
     return
   }
 
@@ -124,6 +125,11 @@ function handleChangeSwiper(e: UniHelper.SwiperOnChangeEvent) {
 
   currentIndex.value = e.detail.current
 }
+function handleJyBanner() {
+  wx.openOfficialAccountArticle({
+    url: 'https://mp.weixin.qq.com/s/lxpdZ6DUhgqg00AT9klu5Q',
+  })
+}
 </script>
 
 <template>
@@ -222,7 +228,7 @@ function handleChangeSwiper(e: UniHelper.SwiperOnChangeEvent) {
       </view>
 
       <view class="mt-20rpx w-full flex items-center justify-between">
-        <image :src="`${StaticUrl}/smqjh-jy-banner.jpg`" class="h-346rpx w-344rpx flex-shrink-0 rounded-12rpx" />
+        <image :src="`${StaticUrl}/smqjh-jy-banner.jpg`" class="h-346rpx w-344rpx flex-shrink-0 rounded-12rpx" @click="handleJyBanner" />
         <view class="flex flex-1 flex-col items-end justify-center" @click="handleGo">
           <image :src="`${StaticUrl}/smqjh-home-banner1.jpg`" class="h-180rpx w-344rpx rounded-12rpx" />
           <image :src="`${StaticUrl}/smqjh-home-banner2.jpg`" class="mt-14rpx h-152rpx w-344rpx rounded-12rpx" />

+ 6 - 6
src/pages/my/index.vue

@@ -13,10 +13,10 @@ definePage({
   },
 })
 const tabList = ref([
-  { title: '待支付', icon: `${StaticUrl}/1.png`, name: 'smqjh-order' },
-  { title: '待收货', icon: `${StaticUrl}/2.png`, name: 'smqjh-order' },
-  { title: '已完成', icon: `${StaticUrl}/6.png`, name: 'smqjh-order' },
-  { title: '退款售后', icon: `${StaticUrl}/3.png`, name: 'common-afterSalesList' },
+  { title: '待支付', icon: `${StaticUrl}/1.png`, name: 'smqjh-order', status: 'paddingPay' },
+  { title: '进行中', icon: `${StaticUrl}/2.png`, name: 'smqjh-order', status: 'ing' },
+  { title: '已完成', icon: `${StaticUrl}/6.png`, name: 'smqjh-order', status: 'completed' },
+  { title: '退款售后', icon: `${StaticUrl}/3.png`, name: 'common-afterSalesList', status: '' },
 ])
 onMounted(() => {
   useUserStore().getUserInfo()
@@ -35,8 +35,8 @@ function handleLoginOut() {
     },
   })
 }
-function handleGo(item: { name: string }) {
-  router.push({ name: item.name })
+function handleGo(item: { name: string, status: string }) {
+  router.push({ name: item.name, params: { status: item.status } })
 }
 </script>
 

+ 17 - 0
src/store/user.ts

@@ -426,5 +426,22 @@ export const useUserStore = defineStore('user', {
       }
       return textMap[order.hbOrderStatus as keyof typeof textMap] || '未知订单状态'
     },
+    async  getOrderNode(orderNumber: string) {
+      uni.showLoading({
+        mask: true,
+      })
+      return new Promise((resolve, reject) => {
+        Apis.xsb.deliveryNode({
+          data: {
+            orderNumber,
+          },
+        }).then((res) => {
+          // NodeList.value = res.data
+          resolve(res.data)
+        }).catch((err) => {
+          reject(err)
+        }).finally(() => uni.hideLoading())
+      })
+    },
   },
 })

+ 52 - 2
src/subPack-smqjh/components/xsb-orderList/xsb-orderList.vue

@@ -9,7 +9,9 @@ defineProps<{
 const _emit = defineEmits<{
   refresh: []
 }>()
-
+const NodeList = ref<Api.DeliveryNode[]>([])
+const showNode = ref(false)
+const plugins = requirePlugin('logisticsPlugin')
 async function handleCancel(order: Api.xsbOrderList) {
   await useUserStore().handleCommonCancelOrder?.(order)
   _emit('refresh')
@@ -46,8 +48,32 @@ async function handleAfterSale(item: Api.xsbOrderList) {
   }
   await useSysStore().getRefunOrder(item.orderNumber as string)
 }
-async function handleTime(_order: Api.xsbOrderList) {
+function handleTime(_order: Api.xsbOrderList) {
 
+}
+async function handleLogistics(_order: Api.xsbOrderList) {
+  // router.push({ name: 'xsb-logistics', params: { id: order.orderNumber } })
+  if (_order.dvyType === 3) {
+    const res = await useUserStore().getOrderNode(String(_order.orderNumber))
+    NodeList.value = res as Api.DeliveryNode[]
+    showNode.value = true
+  }
+  if ([1, 10].includes(Number(_order.dvyType)) && _order.actualTotal) {
+    uni.showLoading({ mask: true })
+    try {
+      const res = await Apis.xsb.getWaybillToken({ data: { orderNumber: String(_order.orderNumber) } })
+      uni.hideLoading()
+      const jsData = JSON.parse(res.data)
+      if (jsData.errmsg === 'ok') {
+        plugins.openWaybillTracking({
+          waybillToken: jsData.waybill_token,
+        })
+      }
+    }
+    catch {
+      uni.hideLoading()
+    }
+  }
 }
 </script>
 
@@ -150,7 +176,31 @@ async function handleTime(_order: Api.xsbOrderList) {
           </wd-button>
         </view>
       </template>
+      <template v-if="[OrderStatus.OrderAccepted, OrderStatus.OrderWaitDelivery, OrderStatus.OrderDelivering].includes(order.hbOrderStatus)">
+        <view class="mr-20rpx">
+          <wd-button size="small" plain type="info" @click.stop="handleLogistics(order)">
+            查看物流
+          </wd-button>
+        </view>
+      </template>
     </view>
+    <Zpopup v-model="showNode" :showfooter="false">
+      <view class="p-24rpx">
+        <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>
 

+ 43 - 7
src/subPack-smqjh/order/index.vue

@@ -14,6 +14,13 @@ const skelet = ref(true)
 const navActiveTab = ref('all')
 const scrollViewId = ref()
 const orderStatusActive = ref('all')
+
+// 缓存各 tab 的数据,key = `${navActiveTab}-${orderStatusActive}`
+const orderCache = new Map<string, Api.xsbOrderList[]>()
+function getCacheKey() {
+  return `${navActiveTab.value}-${orderStatusActive.value}`
+}
+
 const { data: orderList, isLastPage, page, reload } = usePagination((pageNum, pageSize) => Apis.xsb.orderList({
   data: {
     businessType: unref(navActiveTab) === 'all' ? '' : unref(navActiveTab),
@@ -28,18 +35,41 @@ const { data: orderList, isLastPage, page, reload } = usePagination((pageNum, pa
   initialData: [],
   data: res => res.data?.list,
   append: true,
-}).onSuccess(() => skelet.value = false)
+}).onSuccess(() => {
+  skelet.value = false
+  // 数据加载完成后写入缓存(含分页追加的情况)
+  orderCache.set(getCacheKey(), [...orderList.value])
+})
 
+function loadTab() {
+  const key = getCacheKey()
+  if (orderCache.has(key)) {
+    orderList.value = orderCache.get(key)!
+    skelet.value = false
+  }
+  else {
+    skelet.value = true
+    orderList.value = []
+    reload()
+  }
+}
+onLoad((options: any) => {
+  if (options.status) {
+    orderStatusActive.value = options.status
+  }
+})
 function handleChangeTypeNav(value: string) {
-  skelet.value = true
   navActiveTab.value = value
   scrollViewId.value = null
-  orderList.value = []
+  orderStatusActive.value = 'all'
   nextTick(() => scrollViewId.value = value)
-  reload()
+  loadTab()
 }
+
 onShow(() => {
-  // orderList.value = []
+  // 每次页面显示时刷新当前 tab(清除旧缓存保证数据最新)
+  orderCache.delete(getCacheKey())
+  orderList.value = []
   reload()
 })
 onReachBottom(() => {
@@ -48,8 +78,14 @@ onReachBottom(() => {
   }
 })
 function handleChangeStatus(value: string) {
-  skelet.value = true
   orderStatusActive.value = value
+  loadTab()
+}
+
+// 子组件触发刷新时,清除当前 tab 缓存再重新加载
+function handleRefresh() {
+  orderCache.delete(getCacheKey())
+  skelet.value = true
   orderList.value = []
   reload()
 }
@@ -134,7 +170,7 @@ function handleChangeStatus(value: string) {
         <template v-for="item in orderList" :key="item.orderNumber">
           <OrderRenderer
             :order-list="item"
-            @refresh="reload"
+            @refresh="handleRefresh"
           />
         </template>
         <StatusTip v-if="!orderList.length" tip="暂无内容" />

+ 6 - 6
src/subPack-xsb/commonTab/components/my.vue

@@ -3,20 +3,20 @@ import { StaticUrl } from '@/config'
 import router from '@/router'
 
 const tabList = ref([
-  { title: '待支付', icon: `${StaticUrl}/1.png`, name: 'xsb-order' },
-  { title: '待收货', icon: `${StaticUrl}/2.png`, name: 'xsb-order' },
-  { title: '已完成', icon: `${StaticUrl}/6.png`, name: 'xsb-order' },
-  { title: '退款售后', icon: `${StaticUrl}/3.png`, name: 'common-afterSalesList' },
+  { title: '待支付', icon: `${StaticUrl}/1.png`, name: 'xsb-order', status: 'paddingPay' },
+  { title: '进行中', icon: `${StaticUrl}/2.png`, name: 'xsb-order', status: 'ing' },
+  { title: '已完成', icon: `${StaticUrl}/6.png`, name: 'xsb-order', status: 'completed' },
+  { title: '退款售后', icon: `${StaticUrl}/3.png`, name: 'common-afterSalesList', status: '' },
 ])
 
 const { token, userInfo, getUserAvatar } = storeToRefs(useUserStore())
 useUserStore().getUserInfo()
-function handleGo(item: { name: string }) {
+function handleGo(item: { name: string, status: string }) {
   // if (item.name === 'common-afterSalesList') {
   //   useGlobalToast().show('此功能暂未开放')
   //   return
   // }
-  router.push({ name: item.name })
+  router.push({ name: item.name, params: { status: item.status } })
 }
 </script>
 

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

@@ -51,6 +51,11 @@ function handleChangeStatus(value: string) {
   orderList.value = []
   reload()
 }
+onLoad((options: any) => {
+  if (options.status) {
+    orderStatusActive.value = options.status
+  }
+})
 
 onReachBottom(() => {
   if (!isLastPage.value) {
@@ -89,7 +94,7 @@ onShow(() => {
         </view>
       </view>
     </view>
-    <view class="px-24rpx">
+    <view class="px-24rpx pt-20rpx">
       <template v-for="item in orderList" :key="item.orderNumber">
         <xsbOrderList :order="item" @refresh="reload" />
       </template>

+ 25 - 20
src/subPack-xsb/orderDetaile/index.vue

@@ -22,8 +22,9 @@ const mapMarkers = ref<MapMarker[]>([])
 const mapPolyLine = ref<MapPolyline[]>([])
 const showNode = ref(false)
 const NodeList = ref<Api.DeliveryNode[]>([])
-const viewDevyBtn = computed(() => {
-  return orderInfo.value?.actualTotal && orderInfo.value.dvyType === 1
+const refundStatus = ['进行中', '已完成', '退款成功', '已取消', '已驳回']
+const viewDevyBtn = computed(() => { // 2026.3.31,后端说这个字段返回的10是最新的快递,需要兼容1,所以1和10都需要显示
+  return orderInfo.value?.actualTotal && [1, 10].includes(Number(orderInfo.value.dvyType))
 })
 const showMapStatus = [OrderStatus.OrderAccepted, OrderStatus.OrderWaitDelivery, OrderStatus.OrderDelivering, OrderStatus.OrderArrived]
 const mapStaticShopImg = {
@@ -220,28 +221,12 @@ async function getOrderMapInf() {
 
   console.log(mapPolyLine.value, 'mapPolyLine')
 }
-async function getOrderNode() {
-  uni.showLoading({
-    mask: true,
-  })
-  return new Promise((resolve, reject) => {
-    Apis.xsb.deliveryNode({
-      data: {
-        orderNumber: String(orderInfo.value?.orderNumber),
-      },
-    }).then((res) => {
-      NodeList.value = res.data
-      resolve(res.data)
-    }).catch((err) => {
-      reject(err)
-    }).finally(() => uni.hideLoading())
-  })
-}
 
 async function handleMarkerTap(e: UniHelper.MapOnMarkertapEvent) {
   console.log(e, 'sadada')
   if (orderInfo.value?.dvyType === 3) {
-    await getOrderNode()
+    const res = await useUserStore().getOrderNode(String(orderInfo.value?.orderNumber))
+    NodeList.value = res as Api.DeliveryNode[]
     showNode.value = true
   }
   if (viewDevyBtn.value) {
@@ -500,6 +485,26 @@ async function handleReceive() {
           </view>
         </view>
       </view>
+
+      <view v-if="orderInfo.refundOrderList" class="mt-20rpx rounded-16rpx bg-white p-24rpx">
+        <view v-for="item in orderInfo.refundOrderList" :key="item.createTime" class="mb10rpx">
+          <view class="flex items-center justify-between">
+            <view>{{ item.refundOrderStatusDesc }} </view>
+            <view class="flex items-center">
+              <view>查看详情</view>
+              <wd-icon name="arrow-right" size="22px" />
+            </view>
+          </view>
+          <view class="mt10rpx flex items-center text-24rpx text-#AAA">
+            <view>
+              {{ refundStatus[Number(item.refundOrderStatus)] }}
+            </view>
+            <view v-if="[1, 2].includes(Number(item.refundOrderStatus))" class="ml-10rpx">
+              退款金额: {{ item.refundMoney }} 元
+            </view>
+          </view>
+        </view>
+      </view>
       <view class="mt-20rpx rounded-16rpx bg-white p-24rpx">
         <view class="mb-24rpx text-28rpx font-semibold">
           订单信息