85 Angajamente 51b399d50d ... 2c37390ebc

Autor SHA1 Permisiunea de a trimite mesaje. Dacă este dezactivată, utilizatorul nu va putea trimite nici un fel de mesaj Data
  wenjie 2c37390ebc ``` 1 lună în urmă
  zhangtao ffcf0eaa8f fix(config): 修正 trial 环境地址配置 1 lună în urmă
  zhangtao a101cc8f17 fix(config): 调整环境地址配置,修复优惠券退款逻辑 1 lună în urmă
  zhangtao 6c2460bef9 feat(cart): 优化购物车功能与界面细节 1 lună în urmă
  zhangtao 1a306419f0 fix(order): 修复优惠券和购物车相关功能的显示与交互问题 1 lună în urmă
  zhangtao c682ef2293 fix(afterSalesDetail): 修复优惠券访问空指针异常 1 lună în urmă
  zhangtao 48206df613 Merge remote-tracking branch 'origin/master' into zhangtao 1 lună în urmă
  zhangtao e81f38adb2 feat(refund): 优化售后退款详情和优惠券展示 1 lună în urmă
  zouzexu d5b0aef471 ``` 1 lună în urmă
  zouzexu 201d2eb21f Merge branch 'zzx' 1 lună în urmă
  zouzexu f0f4676ef2 fix(attractionsOrderDetail): 修正取消记录显示字段为cancelMemo 1 lună în urmă
  zhangtao a088743034 fix(config): 更新开发环境地址及页面登录状态 1 lună în urmă
  zhangtao af4363bd74 Merge branch 'master' into zhangtao 1 lună în urmă
  zhangtao 23ffcda769 fix(auth): 取消景点详情页的登录限制 1 lună în urmă
  zouzexu 022050e25a ``` 1 lună în urmă
  zhangtao 8b4c5d485a feat(coupon): 优化优惠券功能及支付流程 1 lună în urmă
  zouzexu 107b974742 refactor(attractions): 优化订单详情页面逻辑和状态判断 1 lună în urmă
  zhangtao d384d9c940 fix(cart): 优化购物车功能及界面交互体验 1 lună în urmă
  zouzexu a052b3bebe Merge remote-tracking branch 'origin/master' into zzx 1 lună în urmă
  zhangtao 50daae5275 feat(coupon): 新增优惠券模块及购物车价格明细弹窗 1 lună în urmă
  zouzexu 0a2f9e6bd8 feat(attractions-orderList): 添加订单取消和付款按钮 1 lună în urmă
  zhangtao e469f2afd5 feat(cart): 优化购物车价格明细显示与选择逻辑 1 lună în urmă
  zhangtao 79190a7c73 feat(refueling): 添加取消油卡订单功能 1 lună în urmă
  zouzexu e9e1848b8e feat(attractions): 优化订单详情及支付流程功能 1 lună în urmă
  zouzexu 3840fb4b53 Merge remote-tracking branch 'origin/master' into zzx 1 lună în urmă
  zhangtao 6ea9441c5f fix(config): 修正环境配置中的服务地址 1 lună în urmă
  zouzexu 78fd5a8e92 feat(attractions): 优化订单详情取消及退款逻辑 1 lună în urmă
  zhangtao de613f8c05 fix(order): 优化售后申请按钮显示条件 1 lună în urmă
  zouzexu b42222ab36 feat(attractions): 实现景区订单详情及取消退款功能 1 lună în urmă
  zouzexu d3a35b4c13 ``` 1 lună în urmă
  zouzexu bb57fd4e35 Merge remote-tracking branch 'origin/master' into zzx 1 lună în urmă
  zouzexu b839560e77 ``` 1 lună în urmă
  zhangtao 0805417531 fix(config): 调整开发环境接口地址配置 1 lună în urmă
  zouzexu 471432d9eb ``` 1 lună în urmă
  zhangtao d3d8341997 fix(orderDetaile): 修改退款订单列表展示的字段名称 2 luni în urmă
  zouzexu f1f9ae4b36 ``` 2 luni în urmă
  zhangtao 07dabebcf9 fix(config): 修正开发和试用环境接口配置 2 luni în urmă
  zhangtao 090eacf3a2 Merge remote-tracking branch 'origin/master' into zhangtao 2 luni în urmă
  zhangtao 01867e03e5 fix(order): 优化订单页缓存和分页逻辑并增加退款详情跳转 2 luni în urmă
  zouzexu 9dea2b323f ``` 2 luni în urmă
  zhangtao 6acd69ee61 feat(order): 新增退款订单列表及物流跟踪功能 2 luni în urmă
  zouzexu 6f6ba9b3e2 Merge branch 'master' of http://git.zonelife.cn:3000/zhangtao/city-gather 2 luni în urmă
  zouzexu d0ed4fa371 Merge branch 'zzx' 2 luni în urmă
  zouzexu 19538bd813 ``` 2 luni în urmă
  zhangtao 05da5fb649 Merge branch 'master' into zhangtao 2 luni în urmă
  zhangtao 9f0e405b6e feat(subPack-attractions): 添加景点子包及相关页面配置 2 luni în urmă
  zhangtao 9c189f0a7b refactor(core): 移除景点子包相关页面及样式调整 2 luni în urmă
  zhangtao ecd1d39811 refactor(ui): 调整订单列表间距及首页图片样式 2 luni în urmă
  zouzexu 6adae1671c Merge branch 'zzx' 2 luni în urmă
  zouzexu bba8c382dd Merge branch 'master' of http://git.zonelife.cn:3000/zhangtao/city-gather 2 luni în urmă
  zouzexu 568f6529eb ``` 2 luni în urmă
  zouzexu 93596f9fee ``` 2 luni în urmă
  zouzexu 9b94929552 ``` 2 luni în urmă
  zouzexu 2849e5b011 Merge remote-tracking branch 'origin/master' into zzx 2 luni în urmă
  zouzexu 9c4b14ca99 ``` 2 luni în urmă
  zhangtao 8be55d85fb feat(nav): 新增加油功能并调整加油图标显示规则 2 luni în urmă
  zouzexu 1914678b23 ``` 2 luni în urmă
  zouzexu e0a6a9ca4c ``` 2 luni în urmă
  zouzexu a4f29609da feat(commonTab): 新增景点通用标签页组件 2 luni în urmă
  zouzexu 6d57190104 ``` 2 luni în urmă
  zhangtao 2649c5bcde fix(manifest): 修复manifest.json格式错误 2 luni în urmă
  zhangtao 5a13f2fbf1 Merge remote-tracking branch 'origin/master' into zhangtao 2 luni în urmă
  zhangtao 156c52164d feat(home): 新增Banner详情页并更新首页交互逻辑 2 luni în urmă
  zhangtao 571ec41df7 fix(refueling-webview): 清空导航栏标题并更新支付页面链接 2 luni în urmă
  zouzexu 4cca6bbcb5 ``` 2 luni în urmă
  zouzexu 3c74188fb7 ``` 2 luni în urmă
  zouzexu 44075a1750 Merge remote-tracking branch 'origin/master' into zzx 2 luni în urmă
  zouzexu 5a2860a387 ``` 2 luni în urmă
  zhangtao 977fa477f4 fix(manifest): 修复manifest.json文件末尾格式错误 2 luni în urmă
  zhangtao 7c5f9d181c fix(index): 添加渠道权限限制提示 2 luni în urmă
  zhangtao 4d1a8f1c44 feat(refueling): 新增加油订单详情及列表组件 2 luni în urmă
  zouzexu fd2b6b44ef ``` 2 luni în urmă
  zhangtao ae26dced30 fix(goods): 修复商品图片点击预览功能 2 luni în urmă
  zhangtao 40e52acbb1 feat(router): 支持扫码参数 q 为编码 URL 并解析参数 2 luni în urmă
  zhangtao 05ad07ebdc ``` 2 luni în urmă
  zhangtao b6ea6436a8 ``` 2 luni în urmă
  zouzexu a4e5f84ee9 ``` 2 luni în urmă
  zhangtao 649e37f89e ``` 2 luni în urmă
  zhangtao 0db9fca583 Merge remote-tracking branch 'origin/master' into zhangtao 2 luni în urmă
  zhangtao cd4341af0f feat(subPack-xsb): 商品详情页添加shopId参数 2 luni în urmă
  zouzexu f90c463966 ``` 2 luni în urmă
  zouzexu d6cb398b63 Merge remote-tracking branch 'origin/master' into zzx 3 luni în urmă
  zouzexu 1c037c073b ``` 3 luni în urmă
  zouzexu e29dae5935 ``` 3 luni în urmă
  zhangtao 1f23c901a5 refactor(components): 移除WdCurtain组件类型声明 3 luni în urmă
75 a modificat fișierele cu 6797 adăugiri și 377 ștergeri
  1. 2 1
      .vscode/settings.json
  2. 4 0
      async-component.d.ts
  3. 1282 0
      src/api/api.type.d.ts
  4. 25 1
      src/api/apiDefinitions.ts
  5. 282 15
      src/api/globals.d.ts
  6. 6 0
      src/auto-imports.d.ts
  7. 1 0
      src/components.d.ts
  8. 3 3
      src/config/index.ts
  9. 109 2
      src/pages.json
  10. 82 23
      src/pages/cart/index.vue
  11. 36 8
      src/pages/index/index.vue
  12. 6 6
      src/pages/my/index.vue
  13. 74 2
      src/router/index.ts
  14. 2 0
      src/store/address.ts
  15. 23 8
      src/store/cart.ts
  16. 22 2
      src/store/user.ts
  17. 271 0
      src/subPack-attractions/attractionsDetail/attractionsDetail.vue
  18. 466 0
      src/subPack-attractions/attractionsOrderDetail/attractionsOrderDetail.vue
  19. 30 0
      src/subPack-attractions/attractionsOrderDetail/orderDetail-data.ts
  20. 110 0
      src/subPack-attractions/attractionsOrderPay/attractionsOrderPay.vue
  21. 341 0
      src/subPack-attractions/attractionsReservation/attractionsReservation.vue
  22. 24 0
      src/subPack-attractions/attractionsReservation/reservation-data.ts
  23. 128 0
      src/subPack-attractions/commonTab/components/homeList.vue
  24. 77 0
      src/subPack-attractions/commonTab/components/orderList.vue
  25. 54 0
      src/subPack-attractions/commonTab/index.vue
  26. 1 0
      src/subPack-attractions/commonTab/order-data.ts
  27. 237 0
      src/subPack-attractions/components/DatePicker.vue
  28. 50 0
      src/subPack-attractions/components/orderDetailStatus.vue
  29. 74 0
      src/subPack-charge/chargeAddPlate/chargeAddPlate.vue
  30. 97 0
      src/subPack-charge/chargeBuyaTicketList/chargeBuyaTicketList.vue
  31. 19 3
      src/subPack-charge/chargeOrderDetail/chargeOrderDetail.vue
  32. 106 0
      src/subPack-charge/chargePlateList/chargePlateList.vue
  33. 39 1
      src/subPack-charge/chargeStart/chargeStart.vue
  34. 198 0
      src/subPack-charge/chargeVoucher/chargeVoucher.vue
  35. 392 0
      src/subPack-charge/components/plate/index.vue
  36. 158 33
      src/subPack-charge/index/index.vue
  37. 2 2
      src/subPack-common/afterSales/index.vue
  38. 100 10
      src/subPack-common/afterSalesDetail/index.vue
  39. 2 6
      src/subPack-common/integral/index.vue
  40. 2 1
      src/subPack-film/index/index.vue
  41. 2 2
      src/subPack-film/movie/index.vue
  42. 47 0
      src/subPack-refueling/commonTab/index.vue
  43. 277 0
      src/subPack-refueling/orderDetaile/index.vue
  44. 2 2
      src/subPack-refueling/webView/index.vue
  45. 17 0
      src/subPack-smqjh/bannerDetaile/index.vue
  46. 85 0
      src/subPack-smqjh/components/attractions-orderList/attractions-orderList.vue
  47. 1 1
      src/subPack-smqjh/components/charge-orderList/charge-orderList.vue
  48. 1 1
      src/subPack-smqjh/components/film-orderList/film-orderList.vue
  49. 138 0
      src/subPack-smqjh/components/refueling-order/index.vue
  50. 1 1
      src/subPack-smqjh/components/videoRights-orderList/videoRights-orderList.vue
  51. 55 3
      src/subPack-smqjh/components/xsb-orderList/xsb-orderList.vue
  52. 7 0
      src/subPack-smqjh/order/components/OrderRenderer.vue
  53. 57 8
      src/subPack-smqjh/order/index.vue
  54. 2 1
      src/subPack-smqjh/order/order-data.ts
  55. 16 5
      src/subPack-videoRights/commonTab/components/home.vue
  56. 2 2
      src/subPack-videoRights/commonTab/components/order.vue
  57. 1 1
      src/subPack-videoRights/videoRightsDetail/videoRightsDetail.vue
  58. 25 1
      src/subPack-videoRights/videoRightsOrderInfo/videoRightsOrderInfo.vue
  59. 1 1
      src/subPack-videoRights/videoRightsSubmitOrder/videoRightsSubmitOrder.vue
  60. 83 22
      src/subPack-xsb/commonTab/components/cart.vue
  61. 318 118
      src/subPack-xsb/commonTab/components/classfiy.vue
  62. 96 0
      src/subPack-xsb/commonTab/components/couponArrivalPopup.vue
  63. 3 3
      src/subPack-xsb/commonTab/components/index.vue
  64. 12 7
      src/subPack-xsb/commonTab/components/my.vue
  65. 26 2
      src/subPack-xsb/commonTab/index.vue
  66. 99 0
      src/subPack-xsb/components/coupon/index.vue
  67. 188 23
      src/subPack-xsb/confirmOrder/index.vue
  68. 84 0
      src/subPack-xsb/coupon/index.vue
  69. 36 20
      src/subPack-xsb/goods/index.vue
  70. 6 1
      src/subPack-xsb/order/index.vue
  71. 47 22
      src/subPack-xsb/orderDetaile/index.vue
  72. 11 2
      src/subPack-xsb/store-xsb/sys.ts
  73. 13 1
      src/uni-pages.d.ts
  74. 100 0
      src/utils/index.ts
  75. 1 0
      vite.config.ts

+ 2 - 1
.vscode/settings.json

@@ -52,5 +52,6 @@
   "explorer.fileNesting.enabled": true,
   "explorer.fileNesting.patterns": {
     "vite.config.*": "pages.config.*, manifest.config.*, uno.config.*, volar.config.*, *.env, .env.*, alova.config.*"
-  }
+  },
+  "i18n-ally.localesPaths": []
 }

+ 4 - 0
async-component.d.ts

@@ -34,3 +34,7 @@ declare module '@/subPack-smqjh/components/djk-order/index.vue?async' {
   const component: typeof import('@/subPack-smqjh/components/djk-order/index.vue')
   export = component
 }
+declare module '@/subPack-smqjh/components/attractions-orderList/attractions-orderList.vue?async' {
+  const component: typeof import('@/subPack-smqjh/components/attractions-orderList/attractions-orderList.vue')
+  export = component
+}

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

@@ -558,6 +558,7 @@ namespace Api {
     [property: string]: any
   }
   interface xsbOrderList {
+
     /**
      * 售后状态
      * 2退款已完成
@@ -740,6 +741,129 @@ 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 {
+    /**
+     * 小桔订单号
+     */
+    xjOrderId?: string
+    /**
+     * 门店ID
+     */
+    storeId?: string
+    /**
+     * 门店名称
+     */
+    storeName?: string
+    /**
+     * 城市名称
+     */
+    cityName?: string
+    /**
+     * 枪号
+     */
+    gunNo?: string
+    /**
+     * 商品名称(油品名称)
+     */
+    itemName?: string
+    /**
+     * 油号id
+     */
+    itemId?: number
+    /**
+     * 商品升数
+     */
+    quantity?: number
+    /**
+     * 订单金额(单位:分)
+     */
+    totalMoney?: number
+    /**
+     * 实付金额(单位:分)
+     */
+    realMoney?: number
+    /**
+     * 订单状态:1-待支付,2-已支付,6-已退款,9-已取消
+     */
+    orderStatus?: number
+    /**
+     * 支付时间
+     */
+    payTime?: string
+    /**
+     * 退款时间
+     */
+    refundTime?: string
+    /**
+     * 服务费
+     */
+    serviceFee?: number
+    /**
+     * 优惠减免金额
+     */
+    promotionAmount?: number
+    /**
+     * 发改委价格、国标价
+     */
+    cityPrice?: number
+    /**
+     * 门店价、油枪价、油机价
+     */
+    storePrice?: number
+    /**
+     * 品牌名称
+     */
+    brandName?: string
+    /**
+     * 油站地址
+     */
+    address?: string
+    /**
+     * 优惠卷名称
+     */
+    activityName?: string
+    /**
+     * 优惠卷id
+     */
+    allowanceId?: string
+    /**
+     * 小桔H5支付地址
+     */
+    payUrl?: string
+    /**
+     * 小桔价、折扣价、优惠价
+     */
+    vipPrice?: number
   }
   interface OmsDjkOrderAttach {
     /**
@@ -1042,6 +1166,16 @@ namespace Api {
      * skuList
      */
     skuList: CartSkuVo[]
+    /**
+     * 金额
+     */
+    amount: number
+    /**
+     * 优惠券
+     */
+    coupon: number
+    couponName: string
+    orderCouponItemDTOS?: AppMemberCouponVO[]
     [property: string]: any
   }
   interface AppletOrderSkuVo {
@@ -2101,6 +2235,10 @@ namespace Api {
      * 电站名称
      */
     powerStationName: ?string | undefined
+    /**
+     * 车牌
+     */
+    plateNum?: string
   }
 
   interface chargeSearchList {
@@ -2334,7 +2472,117 @@ namespace Api {
      * 充电电费
      */
     totalMoney?: number
+    /**
+     * 用户实付
+     */
+    actualTotal?: number
+    platformVolume?: number
+
+  }
+
+  export interface RechargeLevel {
+    createTime?: string
+    /**
+     * 逻辑删除(0-未删除 1-已删除)
+     */
+    isDeleted?: number
+    /**
+     * 充值金额
+     */
+    money?: number
+    /**
+     * 充值档位名称
+     */
+    name?: string
+    /**
+     * 状态 (0-不可用 1-可用)
+     */
+    status?: number
+    /**
+     * 充值提示
+     */
+    tips?: string
+    updateTime?: string
+  }
+
+  export interface chargeLevelRequest {
+    /**
+     * 收货人电话
+     */
+    consigneeMobile?: string
+    /**
+     * 收货人姓名
+     */
+    consigneeName?: string
+    /**
+     * 购券金额
+     */
+    couponAmount?: number
+    [property: string]: any
+  }
+
+  export interface PurchaseRecordVO {
+    /**
+     * 购券金额
+     */
+    couponAmount?: number
+    /**
+     * 购卷时间
+     */
+    createTime?: string
+    /**
+     * 购券记录编号
+     */
+    recordNo?: string
+    /**
+     * 记录状态:1-已取消 2-已到账 3-退款
+     */
+    recordStatus?: number
+    [property: string]: any
+  }
 
+  export interface UserVehicleVO {
+    /**
+     * 车辆品牌
+     */
+    brand?: string
+    /**
+     * 车辆颜色
+     */
+    color?: string
+    /**
+     * 创建时间
+     */
+    createTime?: string
+    /**
+     * 车辆ID
+     */
+    id?: number
+    /**
+     * 是否默认车辆(0-否 1-是)
+     */
+    isDefault?: number
+    /**
+     * 车牌号
+     */
+    licensePlate?: string
+    /**
+     * 车辆型号
+     */
+    model?: string
+    /**
+     * 备注
+     */
+    remark?: string
+    /**
+     * 用户ID
+     */
+    userId?: number
+    /**
+     * 车辆类型(1-新能源 2-燃油车 3-混合动力)
+     */
+    vehicleType?: number
+    [property: string]: any
   }
 
   interface videoRightHomePage {
@@ -2792,5 +3040,1039 @@ namespace Api {
     welfareGoodList?: PmsDjkGoods[]
     [property: string]: any
   }
+  /**
+   * 返回数据对象
+   *
+   * DataAppProductDetailVo
+   */
+  export interface DataAppProductDetailVo {
+    list?: AppProductDetailVo[]
+    total?: number
+    [property: string]: any
+  }
+  export interface AppProductDetailVo {
+    /**
+     * 预订详细说明
+     */
+    chargeInclude?: string
+    /**
+     * 地区ID
+     */
+    cityId?: string
+    /**
+     * 景点所在地
+     */
+    cityName?: string
+    /**
+     * 产品介绍,富文本
+     */
+    content?: string
+    /**
+     * 创建时间
+     */
+    createTime?: string
+    /**
+     * 标志图
+     */
+    img?: string
+    /**
+     * 相关主图,多个图片以逗号分隔
+     */
+    imgs?: string
+    /**
+     * 重要提示
+     */
+    importantNote?: string
+    /**
+     * 退改申请类型 0不支持 1支持
+     */
+    isChangeask?: number
+    /**
+     * 是否需要人工确认,0自动确认,1人工确认
+     */
+    isConfirm?: number
+    /**
+     * 1在线支付(预付),0酒店前台现付
+     */
+    isOnlinepay?: number
+    /**
+     * 客人资料要求,1 要求输入每个客人资料,0 只需要输入一个客人资料
+     */
+    isSingle?: number
+    /**
+     * 市场价
+     */
+    marketPrice?: number
+    /**
+     * 最大预定数 0不限制
+     */
+    maxNum?: number
+    /**
+     * 预订详细说明
+     */
+    orderDesc?: string
+    /**
+     * 预订说明
+     */
+    orderPolicy?: string
+    /**
+     * 价格结束时间
+     */
+    priceEndDate?: string
+    /**
+     * 价格开始时间
+     */
+    priceStartDate?: string
+    /**
+     * 产品名称
+     */
+    productName?: string
+    /**
+     * 产品ID
+     */
+    productNo?: number
+    /**
+     * 产品简称
+     */
+    productShortName?: string
+    /**
+     * 限购规则
+     */
+    purchaseLimit?: PurchaseLimitVo
+    /**
+     * 退款说明
+     */
+    refundNote?: string
+    /**
+     * 退款规则列表
+     */
+    refundRules?: RefundRuleVo[]
+    /**
+     * 零售价
+     */
+    salePrice?: number
+    /**
+     * 结算价
+     */
+    settlementPrice?: number
+    /**
+     * 最早游玩日期
+     */
+    startDate?: string
+    /**
+     * 几点之前预定
+     */
+    startTime?: string
+    /**
+     * 产品状态 0在线 1下线
+     */
+    state?: number
+    /**
+     * 已销售数量
+     */
+    ticketCount?: number
+    /**
+     * 更新时间
+     */
+    updateTime?: string
+    /**
+     * 使用说明
+     */
+    userNote?: string
+    /**
+     * 有效期限制具体天数或日期
+     */
+    validityCon?: string
+    /**
+     * 有效期限制,0:游客选定的游玩日期当天有效 1:游客预订日期延后几天有效 2:游客预订日期截止到指定日期有效 3:游客选定日期延后几天有效 4:游客选定日期截止到指定日期有效
+     * 5:指定日期xxx到xxx内有效
+     */
+    validityType?: number
+    /**
+     * 景点地址
+     */
+    viewAddress?: string
+    /**
+     * 景点编号
+     */
+    viewId?: string
+    /**
+     * 纬度
+     */
+    viewLatitude?: string
+    /**
+     * 经度
+     */
+    viewLongitude?: string
+    /**
+     * 景点名称
+     */
+    viewName?: string
+    [property: string]: any
+  }
+
+  /**
+   * 限购规则
+   *
+   * PurchaseLimitVo
+   */
+  export interface PurchaseLimitVo {
+    /**
+     * 证件每天限制
+     */
+    limitCardDay?: number
+    /**
+     * 证件商品限制
+     */
+    limitCardGoods?: number
+    /**
+     * 证件数量限制
+     */
+    limitCardNum?: number
+    /**
+     * 手机每天限制
+     */
+    limitPhoneDay?: number
+    /**
+     * 手机商品限制
+     */
+    limitPhoneGoods?: number
+    /**
+     * 手机数量限制
+     */
+    limitPhoneNum?: number
+    /**
+     * 限制时间类型
+     */
+    limitTimeType?: number
+    /**
+     * 预定限制类型,下单数量校验类型
+     */
+    limitType?: number
+    [property: string]: any
+  }
+
+  /**
+   * 退款规则
+   *
+   * RefundRuleVo
+   */
+  export interface RefundRuleVo {
+    /**
+     * 是否支持订单完成后做强制退改 0不支持 1支持
+     */
+    agreeForcetg?: number
+    /**
+     * 分销商可退款规则
+     */
+    cancelMoneyCodex?: string
+    /**
+     * 有效退款日期,0当天,正数前x天,负数后x天
+     */
+    days?: number
+    /**
+     * 有效退款时间,申请退款需要在这个时间之前
+     */
+    hours?: number
+    /**
+     * 供应商退回规则
+     */
+    returnMoneyCodex?: string
+    [property: string]: any
+  }
+  /**
+   * 返回数据对象
+   *
+   * ProductPriceResponseVo
+   */
+  export interface ProductPriceResponseVo {
+    /**
+     * 产品ID
+     */
+    infoId?: number
+    /**
+     * 是否是多规格 0否 1是
+     */
+    isSpec?: number
+    /**
+     * 线路产品价格
+     */
+    linePrices?: LinePriceVo[]
+    /**
+     * 门票多规格价格
+     */
+    muskuPrices?: MuskuPriceVo[]
+    /**
+     * 抢购产品价格
+     */
+    snapupPrices?: SnapupPriceVo[]
+    /**
+     * 多规格商品价格
+     */
+    specProdPrices?: SpecProdPriceVo[]
+    /**
+     * 多规格信息
+     */
+    specs?: SpecVo[]
+    /**
+     * 门票产品价格
+     */
+    ticketPrices?: TicketPriceVo[]
+    [property: string]: any
+  }
+
+  /**
+   * com.smqjh.pms.model.vo.scenery.ProductPriceResponseVo.LinePriceVo
+   *
+   * LinePriceVo
+   */
+  export interface LinePriceVo {
+    /**
+     * 日期
+     */
+    date?: string
+    /**
+     * 最小团队人数
+     */
+    minTeamNum?: number
+    /**
+     * 价格明细列表
+     */
+    priceDetails?: PriceDetailVo[]
+    /**
+     * 剩余数量
+     */
+    remainNum?: number
+    /**
+     * 单房差价
+     */
+    singleRoomPrice?: number
+    /**
+     * 提前预订天数
+     */
+    startDay?: number
+    /**
+     * 起订数量
+     */
+    startNum?: number
+    /**
+     * 总数量
+     */
+    totalNum?: number
+    [property: string]: any
+  }
+
+  /**
+   * com.smqjh.pms.model.vo.scenery.ProductPriceResponseVo.PriceDetailVo
+   *
+   * PriceDetailVo
+   */
+  export interface PriceDetailVo {
+    /**
+     * 市场价
+     */
+    marketPrice?: number
+    /**
+     * 价格ID
+     */
+    priceId?: number
+    /**
+     * 价格名称
+     */
+    priceName?: string
+    /**
+     * 销售价
+     */
+    salePrice?: number
+    /**
+     * 结算价
+     */
+    settlementPrice?: number
+    [property: string]: any
+  }
+
+  /**
+   * com.smqjh.pms.model.vo.scenery.ProductPriceResponseVo.MuskuPriceVo
+   *
+   * MuskuPriceVo
+   */
+  export interface MuskuPriceVo {
+    /**
+     * 日期
+     */
+    date?: string
+    /**
+     * 剩余数量
+     */
+    num?: number
+    /**
+     * 价格明细列表
+     */
+    priceDetails?: PriceDetailVo[]
+    [property: string]: any
+  }
+
+  /**
+   * com.smqjh.pms.model.vo.scenery.ProductPriceResponseVo.SnapupPriceVo
+   *
+   * SnapupPriceVo
+   */
+  export interface SnapupPriceVo {
+    /**
+     * 总数量
+     */
+    allNum?: number
+    /**
+     * 市场价
+     */
+    marketPrice?: number
+    /**
+     * 剩余数量
+     */
+    num?: number
+    /**
+     * 销售价
+     */
+    salePrice?: number
+    /**
+     * 结算价
+     */
+    settlementPrice?: number
+    /**
+     * 规格ID
+     */
+    specId?: string
+    /**
+     * 规格名称
+     */
+    specName?: string
+    [property: string]: any
+  }
+
+  /**
+   * com.smqjh.pms.model.vo.scenery.ProductPriceResponseVo.SpecProdPriceVo
+   *
+   * SpecProdPriceVo
+   */
+  export interface SpecProdPriceVo {
+    /**
+     * 市场价
+     */
+    marketPrice?: number
+    /**
+     * 剩余数量
+     */
+    num?: number
+    /**
+     * 销售价
+     */
+    salePrice?: number
+    /**
+     * 结算价
+     */
+    settlementPrice?: number
+    /**
+     * 规格ID
+     */
+    specId?: string
+    /**
+     * 规格名称
+     */
+    specName?: string
+    [property: string]: any
+  }
+
+  /**
+   * com.smqjh.pms.model.vo.scenery.ProductDetailResponseVo.SpecVo
+   *
+   * SpecVo
+   */
+  export interface SpecVo {
+    /**
+     * 规格名称
+     */
+    specName?: string
+    /**
+     * 规格值列表
+     */
+    specValues?: { [key: string]: any }[]
+    [property: string]: any
+  }
+
+  /**
+   * com.smqjh.pms.model.vo.scenery.ProductPriceResponseVo.TicketPriceVo
+   *
+   * TicketPriceVo
+   */
+  export interface TicketPriceVo {
+    /**
+     * 日期
+     */
+    date?: string
+    /**
+     * 市场价
+     */
+    marketPrice?: number
+    /**
+     * 剩余数量
+     */
+    num?: number
+    /**
+     * 销售价
+     */
+    salePrice?: number
+    /**
+     * 时段列表
+     */
+    seats?: SeatVo[]
+    /**
+     * 结算价
+     */
+    settlementPrice?: number
+    /**
+     * 票余量状态:充足/紧张/售罄
+     */
+    ticketAvailability?: string
+    [property: string]: any
+  }
+
+  export interface CreateOrderRequest {
+    /**
+     * 结束游玩日期,格式yyyy-MM-dd
+     */
+    endTravelDate?: string
+    /**
+     * 联系人地址
+     */
+    linkAddress?: string
+    /**
+     * 联系人证件号
+     */
+    linkCreditNo?: string
+    /**
+     * 联系人证件类型
+     */
+    linkCreditType?: number
+    /**
+     * 联系人邮件
+     */
+    linkEmail?: string
+    /**
+     * 联系人姓名
+     */
+    linkMan?: string
+    /**
+     * 联系人手机号
+     */
+    linkPhone?: string
+    /**
+     * 预定数量
+     */
+    num: number
+    /**
+     * 订单备注
+     */
+    orderMemo?: string
+    /**
+     * 游玩人信息列表
+     */
+    peoples?: PeopleItem[]
+    /**
+     * 产品名称
+     */
+    productName?: string
+    /**
+     * 产品ID(pms_scenic_product.product_no)
+     */
+    productNo: number
+    /**
+     * 游玩日期,格式yyyy-MM-dd
+     */
+    travelDate: string
+    [property: string]: any
+  }
+  export interface PeopleItem {
+    /**
+     * 是否成人
+     */
+    adult?: boolean
+    /**
+     * 年龄,儿童必传
+     */
+    age?: number
+    /**
+     * 游玩人快递地址
+     */
+    linkAddress?: string
+    /**
+     * 游玩人证件号
+     */
+    linkCreditNo?: string
+    /**
+     * 游玩人证件类型
+     */
+    linkCreditType?: number
+    /**
+     * @Schema(description = "游玩人名")
+     * 游玩人姓名
+     */
+    linkMan?: string
+    /**
+     * 游玩人手机号
+     */
+    linkPhone?: string
+    /**
+     * 性别 1未知 2男 3女
+     */
+    linkSex?: number
+    [property: string]: any
+  }
+  export interface ScenicPayPreviewVo {
+    /**
+     * 用户可用积分
+     */
+    availablePoints?: number
+    /**
+     * 系统订单号
+     */
+    orderNumber?: string
+    /**
+     * 订单总金额(actualTotal)
+     */
+    orderTotal?: number
+    /**
+     * 积分可抵扣金额
+     */
+    pointsDeduct?: number
+    /**
+     * 微信支付金额
+     */
+    wxPayMoney?: number
+    [property: string]: any
+  }
+  export interface DataScenicOrderListVo {
+    list?: ScenicOrderListVo[]
+    total?: number
+    [property: string]: any
+  }
+
+  /**
+   * 景区订单列表项VO
+   *
+   * ScenicOrderListVo
+   */
+  export interface ScenicOrderListVo {
+    /**
+     * 用户实际支付金额
+     */
+    actualTotal?: number
+    /**
+     * 下单时间
+     */
+    createTime?: string
+    /**
+     * 结束游玩日期
+     */
+    endTravelDate?: string
+    /**
+     * 景点图片
+     */
+    img?: string
+    /**
+     * 系统订单号
+     */
+    orderNumber?: string
+    /**
+     * 产品名称
+     */
+    productName?: string
+    /**
+     * 游玩日期
+     */
+    travelDate?: string
+    /**
+     * 景点名称
+     */
+    viewName?: string
+    /**
+     * 订单状态0-待支付
+     * 60-订单已取消
+     * 70-已支付
+     * 80-订单已完成
+     */
+    hbOrderStatus?: number
+    [property: string]: any
+  }
+  export interface ScenicOrderDetailVo {
+    /**
+     * 实际总值(用户实付)
+     */
+    actualTotal?: number
+    /**
+     * 业务类型
+     */
+    businessType?: string
+    /**
+     * 取消原因
+     */
+    cancelReason?: string
+    /**
+     * 取消退款记录列表
+     */
+    cancelRecords?: OmsScenicOrderCancel
+    /**
+     * 取消时间
+     */
+    cancelTime?: string
+    /**
+     * 创建时间
+     */
+    createTime?: string
+    /**
+     * 海博订单状态:0-待支付,20-已接单,30-待配送,40-配送中,50-取消待审核,60-已取消,70-已送达,80-已完成
+     */
+    hbOrderStatus?: number
+    /**
+     * 是否已支付 1:已支付 0:未支付
+     */
+    isPayed?: number
+    /**
+     * 积分抵扣
+     */
+    offsetPoints?: number
+    /**
+     * 订单总额
+     */
+    orderMoney?: number
+    /**
+     * 系统订单号
+     */
+    orderNumber?: string
+    /**
+     * 付款时间
+     */
+    payTime?: string
+    /**
+     * 支付方式 0-微信 1-积分 2-混合
+     */
+    payType?: number
+    /**
+     * 游玩人列表
+     */
+    peoples?: OmsOrderScenicPeople[]
+    /**
+     * 景区子订单
+     */
+    scenicOrder?: OmsOrderScenic
+    [property: string]: any
+  }
+
+  /**
+   * 景区订单取消退款记录
+   *
+   * OmsScenicOrderCancel
+   */
+  export interface OmsScenicOrderCancel {
+    /**
+     * 回调通知时间
+     */
+    callbackTime?: string
+    /**
+     * 第三方取消审核ID(回调匹配用)
+     */
+    cancelId?: string
+    /**
+     * 取消原因
+     */
+    cancelMemo?: string
+    /**
+     * 取消退款金额
+     */
+    cancelMoney?: number
+    /**
+     * 申请取消数量
+     */
+    cancelNum?: number
+    /**
+     * 取消状态 0申请中 1取消成功 2取消被拒绝
+     */
+    cancelState?: number
+    /**
+     * 创建时间
+     */
+    createTime?: string
+    /**
+     * 逻辑删除 0正常 1已删除
+     */
+    deleted?: number
+    /**
+     * 主键ID
+     */
+    id?: number
+    /**
+     * 申请人(会员ID)
+     */
+    memberId?: number
+    /**
+     * 自我游订单号(第三方)
+     */
+    orderId?: number
+    /**
+     * 系统订单号
+     */
+    orderNumber?: string
+    /**
+     * 拒绝原因
+     */
+    rejectReason?: string
+    /**
+     * 更新时间
+     */
+    updateTime?: string
+    [property: string]: any
+  }
+
+  /**
+   * 景区订单游玩人信息表
+   *
+   * OmsOrderScenicPeople
+   */
+  export interface OmsOrderScenicPeople {
+    /**
+     * 是否成人
+     */
+    adult?: number
+    /**
+     * 游玩人年龄,儿童必须要传
+     */
+    age?: number
+    /**
+     * 创建时间
+     */
+    createTime?: string
+    /**
+     * 主键ID
+     */
+    id?: number
+    /**
+     * 游玩人快递地址
+     */
+    linkAddress?: string
+    /**
+     * 游玩人证件号码
+     */
+    linkCreditNo?: string
+    /**
+     * 游玩人证件类型
+     */
+    linkCreditType?: number
+    /**
+     * 游玩人姓名
+     */
+    linkMan?: string
+    /**
+     * 游玩人手机号
+     */
+    linkPhone?: string
+    /**
+     * 性别 1未知 2男 3女
+     */
+    linkSex?: number
+    /**
+     * 自我游订单号
+     */
+    orderId?: number
+    /**
+     * 系统订单号
+     */
+    orderSourceId?: string
+    /**
+     * 核销时间
+     */
+    verifyTime?: string
+    /**
+     * 凭证码(主码)
+     */
+    voucherCode?: string
+    /**
+     * pdf链接
+     */
+    voucherPdfUrl?: string
+    /**
+     * 二维码链接
+     */
+    voucherQrcodeUrl?: string
+    /**
+     * 凭证状态 0正常 1已核销 2已取消 3申请取消中 4取消处理中
+     */
+    voucherStatus?: number
+    /**
+     * 凭证类型 0凭证码 1二维码链接 2pdf 3链接
+     */
+    voucherType?: number
+    /**
+     * 凭证链接
+     */
+    voucherUrl?: string
+    [property: string]: any
+  }
+
+  /**
+   * 景区子订单
+   *
+   * OmsOrderScenic
+   */
+  export interface OmsOrderScenic {
+    /**
+     * 取消订单日期
+     */
+    cancelDate?: string
+    /**
+     * 创建时间
+     */
+    createTime?: string
+    /**
+     * 结束游玩日期
+     */
+    endTravelDate?: string
+    /**
+     * 总验证数量
+     */
+    finishNum?: number
+    /**
+     * 免费取票说明
+     */
+    freeMemo?: string
+    /**
+     * 快递费用
+     */
+    getPrice?: number
+    /**
+     * 快递方式 0免费 1需要配送 3无须配送
+     */
+    getType?: number
+    /**
+     * 主键ID
+     */
+    id?: number
+    /**
+     * 产品标志图
+     */
+    img?: string
+    /**
+     * 联系人地址
+     */
+    linkAddress?: string
+    /**
+     * 联系人证件号码
+     */
+    linkCreditNo?: string
+    /**
+     * 联系人证件类型
+     */
+    linkCreditType?: number
+    /**
+     * 联系人email
+     */
+    linkEmail?: string
+    /**
+     * 联系人姓名
+     */
+    linkMan?: string
+    /**
+     * 联系人电话
+     */
+    linkPhone?: string
+    /**
+     * 零售总价
+     */
+    memOrderMoney?: number
+    /**
+     * 产品预定数量
+     */
+    num?: number
+    /**
+     * 下单日期
+     */
+    orderDate?: string
+    /**
+     * 自我游订单号
+     */
+    orderId?: number
+    /**
+     * 订单备注
+     */
+    orderMemo?: string
+    /**
+     * 结算总价
+     */
+    orderMoney?: number
+    /**
+     * 系统订单号
+     */
+    orderSourceId?: string
+    /**
+     * 订单状态,0新订单(下单成功未确认状态) 1已确认(下单成功已确认状态,已确认状态才能调用支付接口) 2已成功(已支付成功状态) 3已取消 4已完成(已消费状态)
+     */
+    orderState?: number
+    /**
+     * 订单状态2,可能为空;1已通知(已支付未出票状态) 2已快递(产品需要配送时才有的状态) 3已出票
+     */
+    orderState2?: string
+    /**
+     * 产品标题
+     */
+    productName?: string
+    /**
+     * 产品id
+     */
+    productNo?: number
+    /**
+     * 游玩日期
+     */
+    travelDate?: string
+    /**
+     * 更新时间
+     */
+    updateTime?: string
+    /**
+     * 景点名称
+     */
+    viewName?: string
+    /**
+     * 退改申请类型 0不支持 1支持
+     */
+    isChangeask?: number
+    [property: string]: any
+  }
+
+  /** 优惠券领取列表 */
+  interface AppMemberCouponVO {
+    /** 第三方id */
+    allowanceId?: string
+    /** 门槛(单位(元)),满减时的需要达到什么金额,0或undefined表示无门槛 */
+    amountMoney?: number
+    /** 面额(单位(元)),优惠的金额 */
+    discountMoney?: number
+    /** 生效时间 */
+    effectiveTime?: string
+    /** 过期时间 */
+    expirationTime?: string
+    /** id */
+    id?: string
+    /** 锁定的订单ID */
+    lockOrderId?: string
+    /** 锁定状态:0-未锁定/已释放 1-已锁定 */
+    lockStatus?: number
+    /** 券名称 */
+    couponName?: string
+    /** 使用范围描述 */
+    scopeDesc?: string
+    /** 订单使用时间 */
+    orderCreateTime?: string
+    /** 使用状态:1-已使用 2-可使用 5-已过期 7-未生效 */
+    useStatus?: number
+    [property: string]: any
+  }
 
 }

+ 25 - 1
src/api/apiDefinitions.ts

@@ -65,6 +65,9 @@ export default {
   'xsb.refundCancel':['GET', '/smqjh-oms/app-api/v1/refund/cancel'],
   'xsb.refundDetails':['GET', '/smqjh-oms/app-api/v1/refund/findByDetails'],
   'xsb.popupConfig':['GET', '/smqjh-system/app-api/v1/appAdvertInfo/popupConfig'],
+  'xsb.memberCouponPage':['GET', '/smqjh-system/app-api/memberCoupon/getPageList'],
+  'xsb.orderCoupons':['GET', '/smqjh-oms/api/v1/order/orderCoupons'],
+  'xsb.newCouponPopup':['GET', '/smqjh-system/sys-api/coupon/newCouponPopup'],
 
   'common.myShoppingCart':['GET', '/smqjh-oms/app-api/v1/shoppingCart/myShoppingCart'],
   'common.addShoppingCart':['POST', '/smqjh-oms/app-api/v1/shoppingCart/addShoppingCart'],
@@ -99,6 +102,16 @@ export default {
   'charge.stopCharge': ['POST', '/smqjh-system/applet/v1/station/stopCharge'],
   'charge.stationInfoMapList': ['GET', '/smqjh-system/applet/v1/homePage/getStationInfoMapList'],
   'charge.orderDetail': ['GET', '/smqjh-oms/api/v1/order/getChargeOrder'],
+  'charge.getReChargeLevel': ['GET', '/smqjh-system/applet/v1/purchaseRecord/getReChargeLevel'],
+  'charge.addPurchaseRecord': ['POST', '/smqjh-system/applet/v1/purchaseRecord/addPurchaseRecord'],
+  'charge.wxJsApiPay': ['POST', '/smqjh-oms/service/pay/jsapi'],
+  'charge.getPurchaseRecordPage': ['POST', '/smqjh-system/applet/v1/purchaseRecord/getPurchaseRecordPage'],
+  'charge.userCouponRefund':['GET','/smqjh-oms/api/v1/order_cd/userCouponRefund'],
+  'charge.default':['GET','/smqjh-system/applet/v1/vehicle/default'],
+  'charge.vehicleList':['GET','/smqjh-system/applet/v1/vehicle/list'],
+  'charge.addVehicle':['POST','/smqjh-system/applet/v1/vehicle'],
+  'charge.setDefault':['PUT','/smqjh-system/applet/v1/vehicle/default/{id}'],
+  'charge.deleteVehicle':['DELETE','/smqjh-system/applet/v1/vehicle/{id}'],
 
   'videoRight.findAppByPage': ['GET', '/smqjh-pms/app-api/v1/videoProduct/findAppByPage'],
   'videoRight.goodsDetail': ['GET', '/smqjh-pms/app-api/v1/videoProduct/findById'],
@@ -118,6 +131,17 @@ export default {
   'djk.welfareClaimInfoDetaile':['GET', '/smqjh-pms/app-api/v1/welfareClaimInfo/{memberId}'],
   'djk.welfareClaimInfoPopup':['GET', '/smqjh-pms/app-api/v1/welfareClaimInfo/popup/{memberId}'],
   'djk.addWelfareOrder':['POST', '/smqjh-oms/api/v1/djkOrder/addWelfareOrder'],
-  'djk.submitRefund':['POST', '/smqjh-oms/api/v1/djkOrder/submitRefund'],
+  'djk.submitRefund': ['POST', '/smqjh-oms/api/v1/djkOrder/submitRefund'],
 
+  'refueling.getPayCode': ['POST', '/smqjh-pms/api/v1/refueling/generateQrCode'],
+  'refueling.cancelOrder': ['POST', '/smqjh-oms/api/v1/oil/order/cancel'],
+
+  'attractions.findAppByPage':['GET','/smqjh-pms/app-api/v1/sceneryProduct/findAppByPage'],
+  'attractions.detail':['GET','/smqjh-pms/app-api/v1/sceneryProduct/detail'],
+  'attractions.price':['POST','/smqjh-pms/app-api/v1/sceneryProduct/price'],
+  'attractions.createOrder':['POST','/smqjh-oms/app-api/v1/order/scenic/add'],
+  'attractions.payPreview':['GET','/smqjh-oms/app-api/v1/order/scenic/pay-preview'],
+  'attractions.orderList':['GET','/smqjh-oms/app-api/v1/order/scenic/list'],
+  'attractions.orderDetail':['GET','/smqjh-oms/app-api/v1/order/scenic/detail'],
+  'attractions.cancelOrder':['POST','/smqjh-oms/app-api/v1/order/scenic/cancel'],
 };

+ 282 - 15
src/api/globals.d.ts

@@ -205,8 +205,25 @@ declare global {
       ): Alova2Method<listData<Api.sysDict>, 'sys.dictPage', Config>;
     }
     xsb: {
+      orderCoupons<
+        Config extends Alova2MethodConfig<apiResData<Api.AppMemberCouponVO[]>> & {
+          data: {
+            channelId: number;
+            num: number;
+            shopId: number;
+            skuId: number;
+          };
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<Api.AppMemberCouponVO[]>, 'xsb.orderCoupons', Config>;
+      newCouponPopup<
+        Config extends Alova2MethodConfig<apiResData<Api.AppMemberCouponVO[]>> & {}
+      >(
+        config: Config
+      ): Alova2Method<apiResData<Api.AppMemberCouponVO[]>, 'xsb.newCouponPopup', Config>;
       popupConfig<
-        Config extends Alova2MethodConfig<apiResData<any>> & { }
+        Config extends Alova2MethodConfig<apiResData<any>> & {}
       >(
         config: Config
       ): Alova2Method<apiResData<any>, 'xsb.popupConfig', Config>;
@@ -547,6 +564,19 @@ declare global {
       >(
         config: Config
       ): Alova2Method<any, 'xsb.confirmReceipt', Config>;
+
+      memberCouponPage<
+        Config extends Alova2MethodConfig<listData<Api.AppMemberCouponVO>> & {
+          data: {
+            pageNum?: number;
+            pageSize?: number;
+            useStatus?: number;
+            lockStatus?: number;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<listData<Api.AppMemberCouponVO>, 'xsb.memberCouponPage', Config>;
     }
     common: {
       myShoppingCart<
@@ -914,19 +944,115 @@ declare global {
       ): Alova2Method<apiResData<any>, 'charge.stationInfoMapList', Config>;
 
       orderDetail<
-        Config extends Alova2MethodConfig<apiResData<chargeOrderDetail>> & {
+        Config extends Alova2MethodConfig<apiResData<Api.chargeOrderDetail>> & {
           data: {
             orderNumber?: string | null;
           }
         }
       >(
         config: Config
-      ): Alova2Method<apiResData<chargeOrderDetail>, 'charge.orderDetail', Config>;
+      ): Alova2Method<apiResData<Api.chargeOrderDetail>, 'charge.orderDetail', Config>;
+
+      getReChargeLevel<
+        Config extends Alova2MethodConfig<apiResData<Api.RechargeLevel[]>> & {}
+      >(
+        config: Config
+      ): Alova2Method<apiResData<Api.RechargeLevel[]>, 'charge.getReChargeLevel', Config>;
+
+      addPurchaseRecord<
+        Config extends Alova2MethodConfig<apiResData<string>> & {
+          data: {
+            couponAmount?: number;
+            consigneeName?: string;
+            consigneeMobile?: string;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<string>, 'charge.addPurchaseRecord', Config>;
+
+      wxJsApiPay<
+        Config extends Alova2MethodConfig<apiResData<wxpay>> & {
+          data: {
+            orderNumber?: string;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<wxpay>, 'charge.wxJsApiPay', Config>;
+
+      getPurchaseRecordPage<
+        Config extends Alova2MethodConfig<listData<Api.PurchaseRecordVO>> & {
+          data: {
+            /**
+             * 页码
+             */
+            pageNum?: number;
+            /**
+             * 每页记录数
+             */
+            pageSize?: number;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<listData<Api.PurchaseRecordVO>, 'charge.getPurchaseRecordPage', Config>;
+
+      userCouponRefund<
+        Config extends Alova2MethodConfig<apiResData<any>> & {}
+      >(
+        config: Config
+      ): Alova2Method<apiResData<any>, 'charge.userCouponRefund', Config>;
+
+      default<
+        Config extends Alova2MethodConfig<apiResData<Api.UserVehicleVO>> & {}
+      >(
+        config: Config
+      ): Alova2Method<apiResData<Api.UserVehicleVO>, 'charge.default', Config>;
+
+      vehicleList<
+        Config extends Alova2MethodConfig<apiResData<Api.UserVehicleVO[]>> & {}
+      >(
+        config: Config
+      ): Alova2Method<apiResData<Api.UserVehicleVO[]>, 'charge.vehicleList', Config>;
+
+      addVehicle<
+        Config extends Alova2MethodConfig<listData<any>> & {
+          data: {
+            /**
+             * 车牌号
+             */
+            licensePlate: string;
+            /**
+            * 用户ID
+            */
+            userId: number;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<listData<any>, 'charge.addVehicle', Config>;
+
+      setDefault<
+        Config extends Alova2MethodConfig<apiResData<any>> & {
+          pathParams: { id: string };
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<any>, 'charge.setDefault', Config>;
+
+      deleteVehicle<
+        Config extends Alova2MethodConfig<apiResData<any>> & {
+          pathParams: { id: string };
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<any>, 'charge.deleteVehicle', Config>;
     }
 
     videoRight: {
       findAppByPage<
-        Config extends Alova2MethodConfig<apiResData<videoRightHomePage>> & {
+        Config extends Alova2MethodConfig<apiResData<Api.videoRightHomePage>> & {
           data: {
             pageNum?: number;
             pageSize?: number;
@@ -935,48 +1061,48 @@ declare global {
         }
       >(
         config: Config
-      ): Alova2Method<apiResData<videoRightHomePage>, 'videoRight.findAppByPage', Config>;
+      ): Alova2Method<apiResData<Api.videoRightHomePage>, 'videoRight.findAppByPage', Config>;
 
       goodsDetail<
-        Config extends Alova2MethodConfig<apiResData<VideoProductVo>> & {
+        Config extends Alova2MethodConfig<apiResData<Api.VideoProductVo>> & {
           data: {
             id: string
           }
         }
       >(
         config: Config
-      ): Alova2Method<apiResData<VideoProductVo>, 'videoRight.goodsDetail', Config>;
+      ): Alova2Method<apiResData<Api.VideoProductVo>, 'videoRight.goodsDetail', Config>;
 
       goodsPreview<
-        Config extends Alova2MethodConfig<apiResData<VideoProductOrderPreviewVo>> & {
+        Config extends Alova2MethodConfig<apiResData<Api.VideoProductOrderPreviewVo>> & {
           data: {
             id: string
           }
         }
       >(
         config: Config
-      ): Alova2Method<apiResData<VideoProductOrderPreviewVo>, 'videoRight.goodsPreview', Config>;
+      ): Alova2Method<apiResData<Api.VideoProductOrderPreviewVo>, 'videoRight.goodsPreview', Config>;
 
       orderCreate<
-        Config extends Alova2MethodConfig<videoRightCreateOrder> & {
+        Config extends Alova2MethodConfig<Api.videoRightCreateOrder> & {
           data: Api.videoRightsubmitOrder;
         }
       >(
         config: Config
-      ): Alova2Method<videoRightCreateOrder, 'videoRight.orderCreate', Config>;
+      ): Alova2Method<Api.videoRightCreateOrder, 'videoRight.orderCreate', Config>;
 
       orderDetail<
-        Config extends Alova2MethodConfig<apiResData<VirtualOrderDetailVo>> & {
+        Config extends Alova2MethodConfig<apiResData<Api.VirtualOrderDetailVo>> & {
           data: {
             orderNumber: string
           }
         }
       >(
         config: Config
-      ): Alova2Method<apiResData<VirtualOrderDetailVo>, 'videoRight.goodsPreview', Config>;
+      ): Alova2Method<apiResData<Api.VirtualOrderDetailVo>, 'videoRight.goodsPreview', Config>;
 
       orderList<
-        Config extends Alova2MethodConfig<apiResData<DataVirtualOrderListVo>> & {
+        Config extends Alova2MethodConfig<apiResData<Api.DataVirtualOrderListVo>> & {
           data: {
             rechargeStatus?: string
             pageNum?: number
@@ -985,7 +1111,7 @@ declare global {
         }
       >(
         config: Config
-      ): Alova2Method<apiResData<DataVirtualOrderListVo>, 'videoRight.orderList', Config>;
+      ): Alova2Method<apiResData<Api.DataVirtualOrderListVo>, 'videoRight.orderList', Config>;
     }
     djk: {
       appletGoodsList<
@@ -1179,6 +1305,147 @@ declare global {
         config: Config
       ): Alova2Method<apiResData<any>, 'djk.submitRefund', Config>;
     }
+    refueling: {
+      /**
+       * 加油支付码
+       */
+      getPayCode<
+        Config extends Alova2MethodConfig<apiResData<string>> & {
+          data: {
+            /**
+             * 二维码内容
+             */
+            content?: string;
+          };
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<string>, 'refueling.getPayCode', Config>;
+      cancelOrder<
+        Config extends Alova2MethodConfig<apiResData<any>> & {
+         params: {
+            /**
+             * 订单ID
+             */
+            orderId: string;
+          };
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<any>, 'refueling.cancelOrder', Config>;
+    }
+    attractions: {
+      findAppByPage<
+        Config extends Alova2MethodConfig<apiResData<Api.DataAppProductDetailVo>> & {
+          data: {
+            /**
+            * 页码
+            */
+            pageNum?: number;
+            /**
+             * 每页记录数
+             */
+            pageSize?: number;
+            /**
+             * 产品名称(模糊查询)
+             */
+            productName?: string;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<Api.DataAppProductDetailVo>, 'attractions.findAppByPage', Config>;
+
+      detail<
+        Config extends Alova2MethodConfig<apiResData<Api.AppProductDetailVo>> & {
+          data: {
+            /**
+            * 产品id
+            */
+            productNo?: number;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<Api.AppProductDetailVo>, 'attractions.detail', Config>;
+
+      price<
+        Config extends Alova2MethodConfig<apiResData<Api.ProductPriceResponseVo>> & {
+          data: {
+            /**
+             * 产品ID
+             */
+            productNo?: number;
+            /**
+             * 开始日期
+             */
+            travelDate?: string;
+            /**
+             * 结束日期,不传默认只查本月的价格
+             */
+            endTravelDate?: string;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<Api.ProductPriceResponseVo>, 'attractions.price', Config>;
+
+      createOrder<
+        Config extends Alova2MethodConfig<apiResData<any>> & {
+          data: Api.CreateOrderRequest;
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<any>, 'attractions.createOrder', Config>;
+
+      payPreview<
+        Config extends Alova2MethodConfig<apiResData<Api.ScenicPayPreviewVo>> & {
+          data: {
+            /**
+            * 订单号
+            */
+            orderNumber: string;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<Api.ScenicPayPreviewVo>, 'attractions.payPreview', Config>;
+
+      orderList<
+        Config extends Alova2MethodConfig<apiResData<Api.DataScenicOrderListVo>> & {
+          data: {
+            pageNum: number;
+            pageSize: number;
+            /**
+             * 订单状态:0-全部 1-待支付 2-已支付 3-已取消 4-已完成
+             */
+            status: number;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<Api.DataScenicOrderListVo>, 'attractions.orderList', Config>;
+      orderDetail<
+        Config extends Alova2MethodConfig<apiResData<Api.ScenicOrderDetailVo>> & {
+          data: {
+            orderNumber: string;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<Api.ScenicOrderDetailVo>, 'attractions.orderDetail', Config>;
+      cancelOrder<
+        Config extends Alova2MethodConfig<apiResData<void>> & {
+          data: {
+            cancelMemo?: string;
+            cancelNum?: number;
+            orderNumber: string;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<void>, 'attractions.cancelOrder', Config>;
+    }
   }
 }
 var Apis: Apis;

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

@@ -47,7 +47,9 @@ declare global {
   const eagerComputed: typeof import('@vueuse/core')['eagerComputed']
   const effectScope: typeof import('vue')['effectScope']
   const extendRef: typeof import('@vueuse/core')['extendRef']
+  const fixImgStyle: typeof import('./utils/index')['fixImgStyle']
   const getActivePinia: typeof import('pinia')['getActivePinia']
+  const getCityName: typeof import('./utils/index')['getCityName']
   const getCurrentInstance: typeof import('vue')['getCurrentInstance']
   const getCurrentPath: typeof import('./utils/index')['getCurrentPath']
   const getCurrentScope: typeof import('vue')['getCurrentScope']
@@ -111,6 +113,7 @@ declare global {
   const onUnload: typeof import('@dcloudio/uni-app')['onUnload']
   const onUnmounted: typeof import('vue')['onUnmounted']
   const onUpdated: typeof import('vue')['onUpdated']
+  const parseUrlParams: typeof import('./utils/index')['parseUrlParams']
   const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
   const persistPlugin: typeof import('./store/persist')['persistPlugin']
   const phoneFormat: typeof import('./utils/index')['phoneFormat']
@@ -411,7 +414,9 @@ declare module 'vue' {
     readonly eagerComputed: UnwrapRef<typeof import('@vueuse/core')['eagerComputed']>
     readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
     readonly extendRef: UnwrapRef<typeof import('@vueuse/core')['extendRef']>
+    readonly fixImgStyle: UnwrapRef<typeof import('./utils/index')['fixImgStyle']>
     readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
+    readonly getCityName: UnwrapRef<typeof import('./utils/index')['getCityName']>
     readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
     readonly getCurrentPath: UnwrapRef<typeof import('./utils/index')['getCurrentPath']>
     readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
@@ -475,6 +480,7 @@ declare module 'vue' {
     readonly onUnload: UnwrapRef<typeof import('@dcloudio/uni-app')['onUnload']>
     readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
     readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
+    readonly parseUrlParams: UnwrapRef<typeof import('./utils/index')['parseUrlParams']>
     readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']>
     readonly persistPlugin: UnwrapRef<typeof import('./store/persist')['persistPlugin']>
     readonly phoneFormat: UnwrapRef<typeof import('./utils/index')['phoneFormat']>

+ 1 - 0
src/components.d.ts

@@ -36,6 +36,7 @@ declare module 'vue' {
     WdNoticeBar: typeof import('wot-design-uni/components/wd-notice-bar/wd-notice-bar.vue')['default']
     WdNotify: typeof import('wot-design-uni/components/wd-notify/wd-notify.vue')['default']
     WdOverlay: typeof import('wot-design-uni/components/wd-overlay/wd-overlay.vue')['default']
+    WdPicker: typeof import('wot-design-uni/components/wd-picker/wd-picker.vue')['default']
     WdPopup: typeof import('wot-design-uni/components/wd-popup/wd-popup.vue')['default']
     WdRadio: typeof import('wot-design-uni/components/wd-radio/wd-radio.vue')['default']
     WdRadioGroup: typeof import('wot-design-uni/components/wd-radio-group/wd-radio-group.vue')['default']

+ 3 - 3
src/config/index.ts

@@ -6,10 +6,10 @@ const mapEnvVersion = {
   // develop: 'http://192.168.1.101:8080',
   // develop: 'http://192.168.0.157:8080',
   // develop: 'http://192.168.1.253:8080',
-  // develop: 'http://192.168.0.19: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.1.89:8080', // 田
+  // develop: 'http://192.168.1.21:8080', // 田
   // develop: 'http://74949mkfh190.vicp.fun', // 付
   // develop: 'http://47.109.84.152:8081',
   // develop: 'https://5ed0f7cc.r9.vip.cpolar.cn',
@@ -20,7 +20,7 @@ const mapEnvVersion = {
   // trial: "http://192.168.1.166:8080/jeecg-boot",
   // trial: 'http://192.168.0.157:8080',
   trial: 'http://47.109.84.152:8081',
-  // trial: 'http://192.168.1.166:8080',
+  // trial: 'http://192.168.1.166:8080,
   // trial: 'https://smqjh.api.zswlgz.com',
   /**
    * 正式版

+ 109 - 2
src/pages.json

@@ -120,6 +120,14 @@
             "navigationBarTitleText": "提交订单"
           }
         },
+        {
+          "path": "coupon/index",
+          "name": "xsb-coupon",
+          "islogin": true,
+          "style": {
+            "navigationBarTitleText": "优惠券列表"
+          }
+        },
         {
           "path": "goods/index",
           "name": "xsb-goods",
@@ -278,6 +286,14 @@
     {
       "root": "subPack-smqjh",
       "pages": [
+        {
+          "path": "bannerDetaile/index",
+          "name": "bannerDetaile",
+          "islogin": false,
+          "style": {
+            "navigationBarTitleText": "详情"
+          }
+        },
         {
           "path": "order/index",
           "name": "smqjh-order",
@@ -389,6 +405,22 @@
     {
       "root": "subPack-charge",
       "pages": [
+        {
+          "path": "chargeAddPlate/chargeAddPlate",
+          "name": "charge-add-plate",
+          "islogin": false,
+          "style": {
+            "navigationBarTitleText": "添加车牌"
+          }
+        },
+        {
+          "path": "chargeBuyaTicketList/chargeBuyaTicketList",
+          "name": "charge-buy-a-ticket-list",
+          "islogin": false,
+          "style": {
+            "navigationBarTitleText": "购券记录"
+          }
+        },
         {
           "path": "chargeDetail/chargeDetail",
           "name": "charge-detail",
@@ -434,6 +466,14 @@
             "navigationStyle": "custom"
           }
         },
+        {
+          "path": "chargePlateList/chargePlateList",
+          "name": "charge-plate-list",
+          "islogin": true,
+          "style": {
+            "navigationBarTitleText": "车牌管理"
+          }
+        },
         {
           "path": "chargeSearchList/chargeSearchList",
           "name": "cahrge-search-list",
@@ -461,6 +501,15 @@
             "navigationStyle": "custom"
           }
         },
+        {
+          "path": "chargeVoucher/chargeVoucher",
+          "name": "charge-voucher",
+          "islogin": true,
+          "style": {
+            "navigationBarTitleText": "",
+            "navigationStyle": "custom"
+          }
+        },
         {
           "path": "index/index",
           "name": "charge-index",
@@ -498,7 +547,8 @@
           "name": "video-rights-order-info",
           "islogin": true,
           "style": {
-            "navigationBarTitleText": "订单确认"
+            "navigationBarTitleText": "订单确认",
+            "navigationStyle": "custom"
           }
         },
         {
@@ -588,12 +638,69 @@
             "navigationBarTitleText": "加油"
           }
         },
+        {
+          "path": "orderDetaile/index",
+          "name": "refueling-orderDetail",
+          "islogin": true,
+          "style": {
+            "navigationBarTitleText": "订单详情",
+            "navigationStyle": "custom"
+          }
+        },
         {
           "path": "webView/index",
           "name": "refueling-webview",
           "islogin": false,
           "style": {
-            "navigationBarTitleText": "支付"
+            "navigationBarTitleText": ""
+          }
+        }
+      ]
+    },
+    {
+      "root": "subPack-attractions",
+      "pages": [
+        {
+          "path": "attractionsDetail/attractionsDetail",
+          "name": "attractions-detail",
+          "islogin": true,
+          "style": {
+            "navigationBarTitleText": "",
+            "navigationStyle": "custom"
+          }
+        },
+        {
+          "path": "attractionsOrderDetail/attractionsOrderDetail",
+          "name": "attractions-order-detail",
+          "islogin": false,
+          "style": {
+            "navigationBarTitleText": "订单详情",
+            "navigationStyle": "custom"
+          }
+        },
+        {
+          "path": "attractionsOrderPay/attractionsOrderPay",
+          "name": "attractions-order-pay",
+          "islogin": false,
+          "style": {
+            "navigationBarTitleText": "订单支付"
+          }
+        },
+        {
+          "path": "attractionsReservation/attractionsReservation",
+          "name": "attractions-reservation-info",
+          "islogin": true,
+          "style": {
+            "navigationBarTitleText": "预定信息"
+          }
+        },
+        {
+          "path": "commonTab/index",
+          "name": "attractions-tabbar",
+          "islogin": false,
+          "style": {
+            "navigationBarTitleText": "",
+            "navigationStyle": "custom"
           }
         }
       ]

+ 82 - 23
src/pages/cart/index.vue

@@ -14,9 +14,10 @@ definePage({
     disableScroll: true,
   },
 })
-const { cartList, isCartAllChecked, totalProduct } = storeToRefs(useSmqjhCartStore())
+const { cartList, totalProduct } = storeToRefs(useSmqjhCartStore())
 const { smqjhSelectedAddress } = storeToRefs(useUserStore())
 const cartStore = useSmqjhCartStore()
+const priceDetailPopup = ref(false)
 const tab = ref(0)
 const selectAddress = ref(false)
 const navList = ref([
@@ -74,11 +75,20 @@ async function handleSelectAddress() {
           管理
         </view>
       </view>
+      <view v-if="cartList.length" class="mb20rpx flex items-center justify-end">
+        <view class="text-24rpx text-[#AAAAAA]" @click="cartStore.cartDeleteGoods">
+          <wd-icon name="delete" size="24rpx" /> 清空购物车
+        </view>
+      </view>
       <scroll-view scroll-y class="content box-border px-24rpx">
         <view v-for="shop in cartList" :key="shop.shopId" class="mb-24rpx rounded-16rpx bg-white px-24rpx pb-18rpx pt-28rpx">
           <wd-checkbox v-model="shop.AllShopGoods" size="large" @change="cartStore.cartStoreAllChecked($event, shop)">
             <view class="text-28rpx font-semibold">
-              {{ shop.shopName }}
+              {{ shop.shopName }} <text class="text-24rpx text-#AAAAAA">
+                已选 {{ shop.allGoods.length }} 件<text v-if="shop.allGoods.length">
+                  ,总重量约为 {{ totalProduct?.totalWeight || 0 }} kg
+                </text>
+              </text>
             </view>
           </wd-checkbox>
           <view class="mt-20rpx h-2rpx w-full bg-[#F0F0F0]" />
@@ -161,31 +171,29 @@ async function handleSelectAddress() {
     <view v-if="cartList.length" class="fixedShadow fixed bottom-60rpx left-0 z-99 box-border w-full flex items-center justify-between rounded-t-16rpx bg-white px-24rpx pb-60rpx pt-10rpx">
       <view class="ios w-full flex items-center justify-between">
         <view class="flex items-center">
-          <image
-            :src="`${StaticUrl}/cart-lanzi.png`"
-            class="h-100rpx w-100rpx"
-          />
-          <view class="ml-16rpx flex items-center">
-            <wd-checkbox v-model="isCartAllChecked" size="large" @change="cartStore.cartAllChecked">
-              全选
-            </wd-checkbox>
-            <view class="ml-10rpx text-24rpx text-[#FF4A39]" @click="cartStore.cartDeleteGoods">
-              删除
-            </view>
+          <view class="flex items-center">
+            <image :src="`${StaticUrl}/cart-lanzi.png`" class="h-100rpx w-100rpx" />
           </view>
-        </view>
-        <view class="flex items-center">
-          <view class="flex items-center font-semibold">
-            <view class="text-22rpx text-[#222]">
-              总计:
+          <view class="ml-40rpx">
+            <view class="flex items-center">
+              <view class="font-semibold">
+                ¥ {{ totalProduct?.amount || 0 }}
+              </view>
+              <view v-if="totalProduct?.coupon" class="ml-10rpx text-24rpx text-[#FF4A39]">
+                共减¥{{ totalProduct?.coupon }}
+              </view>
+              <view v-if="totalProduct?.amount" class="ml10rpx flex items-center text-24rpx text-gray" @click="priceDetailPopup = !priceDetailPopup">
+                明细 <view class="flex items-center" :class="[priceDetailPopup ? 'rotate-180' : '']">
+                  <wd-icon name="arrow-up" size="24rpx" color="#aaa" />
+                </view>
+              </view>
             </view>
-            <view class="flex items-baseline text-24rpx text-[#FF4A39]">
-              ¥
-              <text class="text-36rpx">
-                {{ totalProduct?.price || '0.00' }}
-              </text>
+            <view class="mt-10rpx text-24rpx text-gray">
+              配送费:¥{{ totalProduct?.transfee || 0 }}
             </view>
           </view>
+        </view>
+        <view class="flex items-center">
           <view class="ml-20rpx w-160rpx">
             <wd-button block size="large" @click="cartStore.cartOrderConfirm">
               结算
@@ -194,6 +202,57 @@ async function handleSelectAddress() {
         </view>
       </view>
     </view>
+    <!-- 价格明细弹窗 -->
+    <Zpopup v-model="priceDetailPopup" :zindex="10" bg="#fff">
+      <view class="ios box-border w-full px-40rpx pb-60rpx pt-36rpx">
+        <view class="mb-40rpx text-center text-32rpx font-semibold">
+          价格明细
+        </view>
+        <view class="flex items-center justify-between py-20rpx">
+          <view class="text-30rpx font-semibold">
+            商品合计
+          </view>
+          <view class="text-30rpx font-semibold">
+            ¥{{ totalProduct?.amount }}
+          </view>
+        </view>
+        <view class="flex items-center justify-between py-20rpx">
+          <view class="text-28rpx text-[#333]">
+            商品总价
+          </view>
+          <view class="text-28rpx text-[#333]">
+            ¥{{ totalProduct?.price }}
+          </view>
+        </view>
+        <template v-if="totalProduct?.coupon">
+          <view class="flex items-center justify-between py-20rpx">
+            <view class="text-28rpx text-[#333]">
+              下单用券共减
+            </view>
+            <view class="text-28rpx text-[#FF4A39] font-semibold">
+              -¥{{ totalProduct?.coupon }}
+            </view>
+          </view>
+          <view v-if="totalProduct?.couponName" class="flex items-center justify-between pb-20rpx">
+            <view class="text-24rpx text-[#AAAAAA]">
+              {{ totalProduct?.couponName }}
+            </view>
+            <view class="text-24rpx text-[#AAAAAA]">
+              -¥{{ totalProduct?.coupon }}
+            </view>
+          </view>
+        </template>
+        <view class="flex items-center justify-between py-20rpx">
+          <view class="text-28rpx text-[#333]">
+            配送费
+          </view>
+          <view class="text-28rpx text-[#333]">
+            ¥{{ totalProduct?.transfee }}
+          </view>
+        </view>
+      </view>
+      <view class="h-200rpx" />
+    </Zpopup>
     <selectAddressTemplate v-model="selectAddress" />
   </view>
 </template>

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

@@ -59,11 +59,12 @@ const navList = computed(() => {
     { icon: `${StaticUrl}/smqjh-index-cd.png`, title: '充电', name: 'charge-index', show: true },
     { icon: `${StaticUrl}/smqjh-sp.png`, title: '电影演出', name: 'film-index', show: true },
     { 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 },
+    { icon: `${StaticUrl}/smqjh-attractions.png`, title: '景区', name: 'attractions-tabbar', show: true },
     { icon: `${StaticUrl}/smqjh-diancan.png`, title: '大牌点餐', name: '', show: !isOnlineAudit.value },
-    { icon: `${StaticUrl}/smqjh-jiayou.png`, title: '加油', name: '', show: !isOnlineAudit.value }, // refueling-tabbar
     { icon: `${StaticUrl}/smqjh-jiudian.png`, title: '酒店民宿', name: '', show: !isOnlineAudit.value },
     { icon: `${StaticUrl}/smqjh-daijia.png`, title: '代驾', name: '', show: !isOnlineAudit.value },
-    { icon: `${StaticUrl}/smqjh-djk.png`, title: '大健康', name: 'djk-homeTabbar', show: true },
   ]
   return list
 })
@@ -81,6 +82,18 @@ function handleClick(name: string) {
     show({ msg: '敬请期待 !' })
     return
   }
+  if (userInfo.value.channelId === 58 && name !== 'xsb-homeTabbar') {
+    show({ msg: '您所属的企业暂无权限查看 !' })
+    return
+  }
+  if (name === 'refueling-tabbar') { // 跳转至公众号
+    ;(wx as any).openOfficialAccountProfile({
+      username: 'gh_6a682fa2ed1d',
+    })
+
+    return
+  }
+
   router.push({ name })
 }
 onShareAppMessage(() => {
@@ -105,13 +118,18 @@ async function getZhUserReceived() {
 }
 
 function handleGo() {
-  show({ msg: '敬请期待 !' })
+  router.push({ name: 'bannerDetaile' })
 }
 function handleChangeSwiper(e: UniHelper.SwiperOnChangeEvent) {
   console.log(e.detail.current, 'e.detail.current')
 
   currentIndex.value = e.detail.current
 }
+function handleJyBanner() {
+  wx.openOfficialAccountArticle({
+    url: 'https://mp.weixin.qq.com/s/lxpdZ6DUhgqg00AT9klu5Q',
+  })
+}
 </script>
 
 <template>
@@ -209,11 +227,11 @@ function handleChangeSwiper(e: UniHelper.SwiperOnChangeEvent) {
         </wd-skeleton>
       </view>
 
-      <view class="mt-20rpx w-full flex items-center justify-between" @click="handleGo">
-        <image :src="`${StaticUrl}/smqjh-fl.png`" class="h-346rpx w-344rpx flex-shrink-0" />
-        <view class="flex flex-1 flex-col items-end justify-center">
-          <image :src="`${StaticUrl}/smqjh-hot-vip.png`" class="h-180rpx w-344rpx" />
-          <image :src="`${StaticUrl}/smqjh-cd.png`" class="mt-14rpx h-152rpx w-344rpx" />
+      <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" @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" />
         </view>
       </view>
       <view class="mt-20rpx">
@@ -276,6 +294,16 @@ function handleChangeSwiper(e: UniHelper.SwiperOnChangeEvent) {
                       商品已售罄
                     </view>
                   </view>
+                  <view
+                    v-if="!item.skuList.some((it) => it.saleStatus)"
+                    class="absolute left-0 top-0 z-1 h-full w-full flex items-center justify-center bg-[rgba(255,255,255,0.6)]"
+                  >
+                    <view
+                      class="h-156rpx w-156rpx flex items-center justify-center rounded-full bg-[rgba(0,0,0,.6)] text-28rpx text-white"
+                    >
+                      商品不可售
+                    </view>
+                  </view>
                 </view>
               </grid-view>
             </scroll-view>

+ 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>
 

+ 74 - 2
src/router/index.ts

@@ -86,6 +86,7 @@ router.afterEach((to, from) => {
 export default router
 /**
  * 将 params 参数拼接到 path 路径后面
+ * 兼容扫码拉起小程序时 q 参数为编码后的 URL 的情况
  * @param path 基础路径
  * @param params 参数对象
  * @returns 拼接后的完整路径
@@ -95,11 +96,26 @@ function appendParamsToPath(path: string, params: Record<string, any>): string {
     return path
   }
   const queryParams: string[] = []
+
   Object.entries(params).forEach(([key, value]) => {
     if (value !== undefined && value !== null) {
-      // 对参数进行 URL 编码
+      const strValue = String(value)
+
+      // 处理扫码参数 q,可能是编码后的 URL
+      if (key === 'q') {
+        const extractedParams = extractParamsFromEncodedUrl(strValue)
+        if (extractedParams) {
+          // 将提取的参数合并到 queryParams
+          Object.entries(extractedParams).forEach(([k, v]) => {
+            queryParams.push(`${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
+          })
+          return
+        }
+      }
+
+      // 普通参数直接编码
       const encodedKey = encodeURIComponent(key)
-      const encodedValue = encodeURIComponent(String(value))
+      const encodedValue = encodeURIComponent(strValue)
       queryParams.push(`${encodedKey}=${encodedValue}`)
     }
   })
@@ -107,3 +123,59 @@ function appendParamsToPath(path: string, params: Record<string, any>): string {
   const queryString = queryParams.join('&')
   return queryString ? `${path}?${queryString}` : path
 }
+
+/**
+ * 从编码后的 URL 中提取查询参数
+ * @param encodedUrl 可能被编码的 URL 字符串
+ * @returns 解析后的查询参数对象,如果不是有效的 URL 则返回 null
+ */
+function extractParamsFromEncodedUrl(encodedUrl: string): Record<string, string> | null {
+  try {
+    // 尝试解码 URL
+    let decodedUrl = encodedUrl
+    // 可能需要多次解码(处理多次编码的情况)
+    while (decodedUrl.includes('%')) {
+      const newDecoded = decodeURIComponent(decodedUrl)
+      if (newDecoded === decodedUrl)
+        break
+      decodedUrl = newDecoded
+    }
+
+    // 检查是否是有效的 URL 格式
+    if (!decodedUrl.includes('http://') && !decodedUrl.includes('https://')) {
+      return null
+    }
+
+    // 提取 ? 后面的查询参数
+    const questionIndex = decodedUrl.indexOf('?')
+    if (questionIndex === -1) {
+      return null
+    }
+
+    const queryString = decodedUrl.substring(questionIndex + 1)
+    if (!queryString) {
+      return null
+    }
+
+    // 解析查询参数
+    const params: Record<string, string> = {}
+    const pairs = queryString.split('&')
+
+    pairs.forEach((pair) => {
+      const equalIndex = pair.indexOf('=')
+      if (equalIndex > 0) {
+        const key = decodeURIComponent(pair.substring(0, equalIndex))
+        const val = decodeURIComponent(pair.substring(equalIndex + 1))
+        params[key] = val
+      }
+      else if (pair) {
+        params[decodeURIComponent(pair)] = ''
+      }
+    })
+
+    return Object.keys(params).length > 0 ? params : null
+  }
+  catch {
+    return null
+  }
+}

+ 2 - 0
src/store/address.ts

@@ -1,4 +1,5 @@
 import { defineStore } from 'pinia'
+import { getCityName } from '@/utils/index'
 
 interface addressState {
   Location: {
@@ -104,6 +105,7 @@ export const useAddressStore = defineStore('address', {
           this.Location.latitude = res.latitude
           this.Location.longitude = res.longitude
           this.name = res.name
+          this.city = getCityName(res.address)
         },
         fail: (e) => {
           console.log('获取地址失败', e)

+ 23 - 8
src/store/cart.ts

@@ -28,6 +28,13 @@ export const useSmqjhCartStore = defineStore('smqjh-cart', {
     getTotalNum(): number {
       return this.cartList.map(it => it.skuList.map(its => its.num)).reduce((a, b) => a.concat(b), []).reduce((a, b) => a + b, 0)
     },
+    /**
+     * 计算星闪豹当前门店分类购物车商品总数量cartDeleteGoods
+     */
+    getShopTotalNum(): number {
+      const shopId = uni.getStorageSync('system-xsb').SelectShopInfo?.shopId
+      return this.cartList.find(it => it.shopId === shopId)?.skuList.map(its => its.num).reduce((a, b) => a + b, 0) || 0
+    },
   },
   actions: {
     /**
@@ -156,12 +163,17 @@ export const useSmqjhCartStore = defineStore('smqjh-cart', {
      * 购物车删除商品逻辑
      */
     cartDeleteGoods() {
+      const delGoods = this.cartList.filter(it => it.allGoods?.length > 0)
+      if (!delGoods.length) {
+        useGlobalToast().show({ msg: '请选择要删除的商品' })
+        return
+      }
       useGlobalMessage().confirm({
         title: '删除商品',
         msg: '是否删除选中的商品?',
-        success: async () => {
-          const delGoods = this.cartList.filter(it => it.allGoods?.length > 0)
-          if (delGoods.length) {
+        zIndex: 99999999999999,
+        success: async ({ action }) => {
+          if (action === 'confirm') {
             await Apis.common.deleteShoppingCart({
               pathParams: {
                 ids: delGoods.map(it => it.allGoods).join(','),
@@ -170,9 +182,6 @@ export const useSmqjhCartStore = defineStore('smqjh-cart', {
             this.getCartList('XSB')
             useGlobalToast().show({ msg: '删除成功!' })
           }
-          else {
-            useGlobalToast().show({ msg: '请选择要删除的商品' })
-          }
         },
       })
     },
@@ -184,11 +193,15 @@ export const useSmqjhCartStore = defineStore('smqjh-cart', {
         useGlobalToast().show({ msg: '移除商品中,请稍后' })
         return
       }
+      console.log(item.num, '===============================')
+
       if (item.num === 1) {
         useGlobalMessage().confirm({
           msg: '是否删除该商品?',
-          success: async () => {
-            await this.addCart(item.skuId, -1, item.shopId, 'XSB')
+          success: async ({ action }) => {
+            if (action === 'confirm') {
+              await this.addCart(item.skuId, -1, item.shopId, 'XSB')
+            }
           },
         })
       }
@@ -238,6 +251,8 @@ export const useSmqjhCartStore = defineStore('smqjh-cart', {
       if (ids.length) {
         const shopIds = ids.map(it => it.allGoods).flat()
         const id = shopIds.join(',')
+        console.log(this.cartList, '==========================')
+
         const res = await this.getCartAddGoodsPrice(id)
         this.totalProduct = res
       }

+ 22 - 2
src/store/user.ts

@@ -156,17 +156,19 @@ export const useUserStore = defineStore('user', {
       })
     }, /**
         *
+        * @param freightFee
         * @param businessType
         * @param dvyType  配送类型 1:快递 2:自提 3:及时配送
-        * @param remarks
         * @param shopId
         * @param orderItemList
+        * @param remarks
+        * @param allowanceId
         * @returns 下单获取待支付订单号
         */
     getOrderPayMent(freightFee: number, businessType: string, dvyType: number, shopId: number, orderItemList: {
       prodCount?: number
       skuId?: number
-    }[], remarks?: string): Promise<string> {
+    }[], remarks?: string, allowanceId?: string): Promise<string> {
       uni.showLoading({ mask: true })
       return new Promise((resolve, reject) => {
         if (!this.selectedAddress) {
@@ -183,6 +185,7 @@ export const useUserStore = defineStore('user', {
             shopId,
             orderItemList,
             remarks,
+            allowanceIds: allowanceId ? [allowanceId] : [],
           },
         }).then((res) => {
           resolve(res.data)
@@ -426,5 +429,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())
+      })
+    },
   },
 })

+ 271 - 0
src/subPack-attractions/attractionsDetail/attractionsDetail.vue

@@ -0,0 +1,271 @@
+<script setup lang="ts">
+import DatePicker from '../components/DatePicker.vue'
+import { StaticUrl } from '@/config'
+import router from '@/router'
+import { fixImgStyle } from '@/utils/index'
+
+const { statusBarHeight, MenuButtonHeight, opcity } = storeToRefs(useSysStore())
+
+const currentSwiper = ref(0)
+
+const selectedDate = ref()
+
+const calendarDataMap = ref<Record<string, Array<{ day: number, status?: string, price?: number, selected?: boolean }>>>({})
+
+const currentYearMonth = ref({ year: new Date().getFullYear(), month: new Date().getMonth() + 1 })
+
+const currentMonthDays = computed(() => {
+  const key = `${currentYearMonth.value.year}-${String(currentYearMonth.value.month).padStart(2, '0')}`
+  return calendarDataMap.value[key] || []
+})
+
+definePage({
+  name: 'attractions-detail',
+  islogin: true,
+  style: {
+    navigationBarTitleText: '',
+    navigationStyle: 'custom',
+  },
+})
+
+const productNo = ref('')
+onLoad((options: any) => {
+  productNo.value = options.productNo || ''
+})
+
+onMounted(() => {
+  opcity.value = 0
+  getDetail()
+})
+
+onPageScroll((e) => {
+  const calculatedOpacity = e.scrollTop / 100
+  opcity.value = Math.min(1, Math.max(0.1, calculatedOpacity))
+})
+
+const attractionDetail = ref<Api.AppProductDetailVo | null>(null)
+
+const swiperList = computed(() => {
+  const imgs = attractionDetail.value?.imgs
+  return imgs ? imgs.split(',').filter(Boolean) : []
+})
+
+async function getDetail() {
+  const res = await Apis.attractions.detail({ data: { productNo: Number(productNo.value) } })
+  attractionDetail.value = res.data
+  await getPrice()
+}
+
+async function getPrice(year?: number, month?: number) {
+  const targetYear = year ?? currentYearMonth.value.year
+  const targetMonth = month ?? currentYearMonth.value.month
+  const startDate = `${targetYear}-${String(targetMonth).padStart(2, '0')}-01`
+  const lastDay = new Date(targetYear, targetMonth, 0).getDate()
+  const endDate = `${targetYear}-${String(targetMonth).padStart(2, '0')}-${lastDay}`
+  useGlobalLoading().loading({
+    msg: '加载中...',
+  })
+  const res = await Apis.attractions.price({
+    data: {
+      productNo: Number(productNo.value),
+      travelDate: startDate,
+      endTravelDate: endDate,
+    },
+  })
+  useGlobalLoading().close()
+  const ticketPrices = res.data?.ticketPrices || []
+  const monthKey = `${targetYear}-${String(targetMonth).padStart(2, '0')}`
+  calendarDataMap.value[monthKey] = ticketPrices.map((item: Api.TicketPriceVo) => ({
+    day: item.date ? new Date(item.date).getDate() : 0,
+    status: item.ticketAvailability,
+    price: item.salePrice,
+  })).filter((item: { day: number }) => item.day > 0)
+}
+
+function handleNav() {
+  uni.openLocation({
+    latitude: Number(attractionDetail.value?.viewLatitude) || 0,
+    longitude: Number(attractionDetail.value?.viewLongitude) || 0,
+    name: attractionDetail.value?.viewName,
+    address: attractionDetail.value?.viewAddress,
+  })
+}
+
+function handleOrder() {
+  router.push({ name: 'attractions-tabbar', params: { tabbar: '1' } })
+}
+
+function handleBook() {
+  router.push({
+    name: 'attractions-reservation-info',
+    params: {
+      productNo: productNo.value,
+      selectDate: selectedDate.value || new Date(),
+      productName: attractionDetail.value?.productName || '',
+      isSingle: String(attractionDetail.value?.isSingle),
+      price: String(attractionDetail.value?.salePrice),
+    },
+  })
+}
+
+function handleDateSelect(e: { date: Date, day: number, item?: any }) {
+  selectedDate.value = e.date
+  console.log('选中日期:', e.date, '日期数据:', e.item)
+}
+
+async function handleMonthChange(e: { year: number, month: number }) {
+  currentYearMonth.value = { year: e.year, month: e.month }
+  const monthKey = `${e.year}-${String(e.month).padStart(2, '0')}`
+  if (!calendarDataMap.value[monthKey]) {
+    await getPrice(e.year, e.month)
+  }
+}
+</script>
+
+<template>
+  <view class="min-h-screen bg-#F5F5F5">
+    <wd-navbar
+      title="景区门票"
+      :custom-style="`background-color: rgba(226, 255, 145, ${opcity})`"
+      :bordered="false"
+      :z-index="9999"
+      safe-area-inset-top
+      left-arrow
+      fixed
+      @click-left="router.back()"
+    />
+    <!-- 轮播图 -->
+    <wd-swiper
+      v-if="swiperList.length"
+      v-model:current="currentSwiper"
+      :list="swiperList"
+      :indicator="false"
+      :height="300"
+      class="w-full"
+    />
+    <view v-else :style="{ height: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 30}px` }" />
+    <!-- 价格标题区域 -->
+    <view class="relative z-10 rounded-t-32rpx bg-white px24rpx pt30rpx -mt-30rpx">
+      <view class="flex items-baseline text-#FF4D3A">
+        <text class="text-24rpx">
+          ¥
+        </text>
+        <text class="mx4rpx text-48rpx font-bold">
+          {{ attractionDetail?.salePrice }}
+        </text>
+        <text class="text-24rpx text-gray">
+          起
+        </text>
+      </view>
+      <view class="mt16rpx text-32rpx font-semibold line-height-[1.4]">
+        {{ attractionDetail?.productName }}
+      </view>
+
+      <!-- 地址导航卡片 -->
+      <view
+        class="mt24rpx h160rpx flex items-center justify-between bg-cover bg-center px24rpx"
+        :style="{ backgroundImage: `url(${StaticUrl}/djk-shop-nav-bg.png)` }"
+      >
+        <view class="w450rpx">
+          <view class="line-clamp-1 text-32rpx font-semibold">
+            {{ attractionDetail?.viewName }}
+          </view>
+          <view class="line-clamp-1 mt12rpx flex items-center text-24rpx text-gray">
+            {{ attractionDetail?.viewAddress }}
+          </view>
+        </view>
+        <view class="flex flex-col items-center justify-center" @click="handleNav">
+          <image
+            :src="`${StaticUrl}/djk-shop-dh.png`"
+            class="h40rpx w40rpx"
+          />
+          <view class="mt8rpx text-24rpx">
+            导航
+          </view>
+        </view>
+      </view>
+
+      <!-- 选择日期 -->
+      <view class="mt24rpx">
+        <DatePicker
+          v-model="selectedDate"
+          title="选择日期"
+          :day-data="currentMonthDays"
+          class="mt24rpx"
+          @select="handleDateSelect"
+          @month-change="handleMonthChange"
+        />
+      </view>
+
+      <!-- 预定须知 -->
+      <view class="mt24rpx pb40rpx">
+        <view class="text-32rpx font-semibold">
+          预定须知
+        </view>
+        <!-- <view class="mt16rpx text-28rpx font-semibold">
+          退款说明
+        </view> -->
+        <view class="mt12rpx text-26rpx text-gray line-height-[1.6]">
+          <view>
+            <rich-text :nodes="fixImgStyle(attractionDetail?.content || '')" />
+          </view>
+          <view class="mt8rpx">
+            <rich-text :nodes="fixImgStyle(attractionDetail?.orderDesc || '')" />
+          </view>
+          <view class="mt8rpx">
+            {{ attractionDetail?.chargeInclude }}
+          </view>
+          <view class="mt8rpx">
+            {{ attractionDetail?.refundNote }}
+          </view>
+          <view class="mt8rpx">
+            {{ attractionDetail?.userNote }}
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <!-- 底部占位 -->
+    <view class="h120rpx" />
+
+    <!-- 底部操作栏 -->
+    <view
+      class="fixed bottom-0 left-0 z-100 box-border w-full flex items-center justify-between border-t border-#eee bg-white px24rpx py16rpx"
+      :style="{ paddingBottom: `${(Number(statusBarHeight) || 44) - 20}px` }"
+    >
+      <view class="flex items-center border-t-#EEEEEE">
+        <view class="relative mr-40rpx flex flex-col items-center">
+          <button open-type="contact" class="zbutton" />
+          <wd-icon name="service" size="24px" color="#666" />
+          <text class="text-22rpx text-#666">
+            客服
+          </text>
+        </view>
+        <view class="flex flex-col items-center" @click="handleOrder">
+          <wd-icon name="list" size="22px" color="#666" />
+          <text class="mt4rpx text-22rpx text-#666">
+            订单
+          </text>
+        </view>
+      </view>
+      <view
+        class="h80rpx w-400rpx flex items-center justify-center rounded-40rpx bg-#9ED605 px80rpx text-28rpx text-#FFF font-semibold"
+        @click="handleBook"
+      >
+        立即预定
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+.zbutton{
+  all: unset;
+  width: 70rpx;
+  height: 70rpx;
+  position: absolute;
+  &::after{
+    border: none;
+  }
+}
+</style>

+ 466 - 0
src/subPack-attractions/attractionsOrderDetail/attractionsOrderDetail.vue

@@ -0,0 +1,466 @@
+<script setup lang="ts">
+import OrderDetailStatus from '../components/orderDetailStatus.vue'
+import { columns as creditTypeColumns } from '../attractionsReservation/reservation-data'
+import { orderStatus } from './orderDetail-data'
+import { StaticUrl } from '@/config'
+import router from '@/router'
+
+definePage({
+  name: 'attractions-order-detail',
+  islogin: false,
+  style: {
+    navigationBarTitleText: '订单详情',
+    navigationStyle: 'custom',
+  },
+})
+const { statusBarHeight, MenuButtonHeight, opcity } = storeToRefs(useSysStore())
+const orderNumber = ref('')
+const orderInfo = ref<Api.ScenicOrderDetailVo>()
+const orderPopup = ref(false)
+const refundNum = ref(1)
+const refundReason = ref('')
+const ispay = ref('')
+
+const statusValue = computed(() => orderInfo.value?.hbOrderStatus ?? 0)
+
+/** 当前状态配置 */
+const currentStatus = computed(() => {
+  return orderStatus.find(item => item.value === statusValue.value) || orderStatus[0]
+})
+
+/** 有凭证码的游客列表 */
+const voucherPeoples = computed(() => {
+  return orderInfo.value?.peoples?.filter(p => p.voucherCode) ?? []
+})
+
+/** 是否展示凭证区域 */
+const hasVoucher = computed(() => voucherPeoples.value.length > 0)
+
+const swiperCurrent = ref(0)
+function onSwiperChange(e: any) {
+  swiperCurrent.value = e.detail.current
+}
+
+/** 证件类型文本 */
+function creditTypeText(type?: number) {
+  return creditTypeColumns.value.find(item => item.value === type)?.label ?? '身份证'
+}
+
+async function getData(orderNo: string) {
+  const res = await Apis.attractions.orderDetail({ data: { orderNumber: orderNo } })
+  orderInfo.value = res.data
+}
+
+function handleRefundMinus() {
+  if (refundNum.value > 1)
+    refundNum.value--
+}
+
+function handleRefundPlus() {
+  const maxNum = orderInfo.value?.scenicOrder?.num ?? 1
+  if (refundNum.value < maxNum)
+    refundNum.value++
+}
+
+function handleGoPay() {
+  router.push({
+    name: 'attractions-order-pay',
+    params: {
+      productName: orderInfo.value?.scenicOrder?.productName || '',
+      num: String(orderInfo.value?.scenicOrder?.num) || '',
+      orderNo: orderInfo.value?.orderNumber || '',
+    },
+  })
+}
+
+function handleRebook() {
+  router.push({
+    name: 'attractions-detail',
+    params: {
+      productNo: String(orderInfo.value?.scenicOrder?.productNo),
+    },
+  })
+}
+
+/** 是否显示取消/退款按钮 */
+const showCancelOrRefundButton = computed(() => {
+  const status = statusValue.value
+  const cancelState = orderInfo.value?.cancelRecords?.cancelState
+  const isChangeask = orderInfo.value?.scenicOrder?.isChangeask
+
+  // 必须允许退改申请
+  if (isChangeask !== 1)
+    return false
+
+  // 状态为0(待支付)时可以取消订单
+  if (status === 0)
+    return true
+
+  // 状态为60(已退款)或80(已完成)时不显示
+  if (status === 60 || status === 80)
+    return false
+
+  // 其他状态:没有退款申请或申请已通过时可以再次申请
+  return cancelState == null || cancelState === 1
+})
+
+const isCancel = computed(() => statusValue.value === 0)
+
+/** 取消订单 / 申请退款 */
+async function handleCancelOrder() {
+  orderPopup.value = false
+  useGlobalMessage().confirm({
+    title: '提示',
+    msg: `确定要${isCancel.value ? '取消订单' : '退款'}吗?`,
+    success: async () => {
+      if (isCancel.value) {
+        await Apis.attractions.cancelOrder({
+          data: {
+            orderNumber: orderInfo.value?.orderNumber || '',
+            cancelNum: refundNum.value,
+            cancelMemo: refundReason.value || undefined,
+          },
+        })
+      }
+      else {
+        await Apis.xsb.applyRefund({
+          data: {
+            businessType: 2,
+            buyerDesc: refundReason.value || undefined,
+            orderNumber: orderInfo.value?.orderNumber || '',
+          },
+        })
+      }
+      refundNum.value = 1
+      refundReason.value = ''
+      useGlobalToast().show({ msg: '操作成功' })
+      await getData(orderNumber.value)
+    },
+  })
+}
+
+onLoad((options: any) => {
+  orderNumber.value = options.orderNo
+  ispay.value = options.ispay
+  getData(options.orderNo)
+})
+onMounted(() => {
+  opcity.value = 0
+})
+
+onPageScroll((e) => {
+  const calculatedOpacity = e.scrollTop / 100
+  opcity.value = Math.min(1, Math.max(0.1, calculatedOpacity))
+})
+
+function orderDetailBack() {
+  const vrIndex = 'subPack-attractions/commonTab/index'
+  const pages = getCurrentPages()
+  const targetPageIndex = pages.findIndex(page => page.route === vrIndex)
+  if (ispay.value === 'true') {
+    if (targetPageIndex !== -1) {
+      const delta = pages.length - targetPageIndex - 1
+      router.back({ delta, animationType: 'fade-out' })
+    }
+  }
+  else {
+    router.back()
+  }
+}
+</script>
+
+<template>
+  <view class="order-detail-page">
+    <wd-navbar
+      title="景区门票"
+      :custom-style="`background-color: rgba(226, 255, 145, ${opcity})`"
+      :bordered="false"
+      :z-index="9999"
+      safe-area-inset-top
+      left-arrow
+      fixed
+      @click-left="orderDetailBack"
+    />
+    <view :style="{ height: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 30}px` }" />
+    <view v-if="orderInfo" class="px-24rpx">
+      <view class="rounded-16rpx bg-#FFF p-24rpx">
+        <OrderDetailStatus v-if="!hasVoucher || orderInfo.hbOrderStatus == 60" :status="statusValue" @pay="handleGoPay" @rebook="handleRebook" />
+        <view class="flex items-center justify-between gap-50rpx text-32rpx font-bold">
+          <view>{{ orderInfo.scenicOrder?.productName }}</view>
+          <view class="w-100rpx text-24rpx text-#9ED605" @click="router.push({ name: 'attractions-detail', params: { productNo: String(orderInfo.scenicOrder?.productNo) } })">
+            详情
+          </view>
+        </view>
+        <view v-if="hasVoucher && orderInfo.hbOrderStatus != 60" class="text-center">
+          <view class="mt-24rpx h-2rpx w-full bg-#F0F0F0" />
+          <view class="mt-24rpx flex items-center justify-center gap-10rpx">
+            <wd-icon :name="currentStatus.icon" size="20px" :color="currentStatus.color" />
+            <text class="text-32rpx font-bold" :style="{ color: currentStatus.color }">
+              {{ currentStatus.label }}
+            </text>
+          </view>
+          <swiper
+            :style="{ height: '340rpx' }"
+            :current="swiperCurrent"
+            :indicator-dots="false"
+            @change="onSwiperChange"
+          >
+            <swiper-item v-for="(people, idx) in voucherPeoples" :key="idx" class="flex flex-col items-center justify-center">
+              <view class="flex items-center justify-center">
+                <QCode class="mb-20rpx rounded-16rpx" :text="people.voucherCode || ''" :qwidth="80" :qr-key="`qr-${idx}`" />
+              </view>
+              <view class="text-28rpx font-bold">
+                {{ people.voucherCode }}
+              </view>
+              <view class="mt-8rpx text-24rpx text-#AAAAAA">
+                ({{ people.voucherStatus === 1 ? '已核销' : '未核销' }})
+              </view>
+            </swiper-item>
+          </swiper>
+          <view class="text-24rpx text-#AAAAAA">
+            {{ currentStatus.desc }}
+          </view>
+          <view class="mt-24rpx flex items-center justify-center gap-10rpx" @click="router.push({ name: 'attractions-tabbar', params: { tabbar: '1' } })">
+            <text class="text-28rpx">
+              查看订单列表
+            </text>
+            <wd-icon name="chevron-right" size="20px" />
+          </view>
+        </view>
+      </view>
+      <view class="mt-20rpx rounded-16rpx bg-#FFF p-24rpx">
+        <view class="mt-28rpx flex items-center justify-between">
+          <view class="text-28rpx text-#AAAAAA">
+            订单编号
+          </view>
+          <view class="text-28rpx font-bold">
+            {{ orderInfo.orderNumber }}
+          </view>
+        </view>
+        <view class="mt-28rpx flex items-center justify-between">
+          <view class="text-28rpx text-#AAAAAA">
+            下单时间
+          </view>
+          <view class="text-28rpx font-bold">
+            {{ orderInfo.createTime }}
+          </view>
+        </view>
+        <view class="mt-28rpx flex items-center justify-between">
+          <view class="text-28rpx text-#AAAAAA">
+            游玩时间
+          </view>
+          <view class="text-28rpx font-bold">
+            {{ orderInfo.scenicOrder?.travelDate }} 当天
+          </view>
+        </view>
+        <view v-if="orderInfo.scenicOrder?.orderMemo" class="mt-28rpx rounded-16rpx bg-#F1F1F1 p-24rpx text-28rpx text-#AAAAAA">
+          备注:{{ orderInfo.scenicOrder?.orderMemo }}
+        </view>
+      </view>
+      <view class="mt-20rpx rounded-16rpx bg-#FFF p-24rpx">
+        <view class="flex items-center justify-between">
+          <view class="text-bold text-28rpx">
+            订单总金额
+          </view>
+          <view class="text-bold text-32rpx text-#FF4A39">
+            <text class="text-24rpx">
+              ¥
+            </text>
+            {{ orderInfo.orderMoney }}
+          </view>
+        </view>
+        <view v-if="orderInfo.offsetPoints" class="mt-20rpx flex items-center justify-between text-24rpx">
+          <view>积分扣减</view>
+          <view class="text-#FF4A39">
+            ¥{{ orderInfo.offsetPoints }}
+          </view>
+        </view>
+        <view class="mt-20rpx flex items-center justify-between text-24rpx">
+          <view>微信支付</view>
+          <view class="text-#FF4A39">
+            ¥{{ orderInfo.actualTotal }}
+          </view>
+        </view>
+      </view>
+      <view class="mt-20rpx rounded-16rpx bg-#FFF p-24rpx">
+        <view class="text-28rpx font-bold">
+          联系人资料
+        </view>
+        <view class="mt-24rpx flex items-center gap-92rpx">
+          <view class="text-24rpx">
+            联系人
+          </view>
+          <view class="text-bold text-24rpx">
+            {{ orderInfo.scenicOrder?.linkMan }}
+          </view>
+        </view>
+        <view class="mt-24rpx h-2rpx w-full bg-#F0F0F0" />
+        <view class="mt-24rpx flex items-center gap-92rpx">
+          <view class="text-24rpx">
+            手机号码
+          </view>
+          <view class="text-bold text-24rpx">
+            {{ orderInfo.scenicOrder?.linkPhone }}
+          </view>
+        </view>
+      </view>
+      <!-- 游客信息 -->
+      <view v-for="(people, idx) in orderInfo.peoples" :key="idx" class="mt-20rpx rounded-16rpx bg-#FFF p-24rpx">
+        <view class="text-28rpx font-bold">
+          游客信息{{ (orderInfo.peoples?.length ?? 0) > 1 ? ` ${idx + 1}` : '' }}
+        </view>
+        <view class="mt-24rpx flex items-center gap-92rpx">
+          <view class="text-24rpx">
+            游客姓名
+          </view>
+          <view class="text-bold text-24rpx">
+            {{ people.linkMan }}
+          </view>
+        </view>
+        <view class="mt-24rpx h-2rpx w-full bg-#F0F0F0" />
+        <view class="mt-24rpx flex items-center gap-92rpx">
+          <view class="text-24rpx">
+            证件类型
+          </view>
+          <view class="text-bold text-24rpx">
+            {{ creditTypeText(people.linkCreditType) }}
+          </view>
+        </view>
+        <view class="mt-24rpx h-2rpx w-full bg-#F0F0F0" />
+        <view class="mt-24rpx flex items-center gap-92rpx">
+          <view class="text-24rpx">
+            证件号码
+          </view>
+          <view class="text-bold text-24rpx">
+            {{ people.linkCreditNo }}
+          </view>
+        </view>
+      </view>
+      <view v-if="orderInfo.cancelRecords?.cancelId" class="mt-20rpx rounded-16rpx bg-#FFF p-24rpx">
+        <view class="text-28rpx font-bold">
+          退款申请
+        </view>
+        <view class="mt-28rpx flex items-center justify-between">
+          <view class="text-28rpx text-#AAAAAA">
+            申请时间
+          </view>
+          <view class="text-28rpx font-bold">
+            {{ orderInfo.cancelRecords?.createTime }}
+          </view>
+        </view>
+        <view class="mt-28rpx flex items-center justify-between">
+          <view class="text-28rpx text-#AAAAAA">
+            申请说明
+          </view>
+          <view class="text-28rpx font-bold">
+            {{ orderInfo.cancelRecords?.cancelMemo || '--' }}
+          </view>
+        </view>
+        <view class="mt-28rpx flex items-center justify-between">
+          <view class="text-28rpx text-#AAAAAA">
+            处理状态
+          </view>
+          <view v-if="orderInfo.cancelRecords" class="text-28rpx font-bold">
+            <text v-if="orderInfo.cancelRecords?.cancelState === 0">
+              申请中
+            </text>
+            <text v-if="orderInfo.cancelRecords?.cancelState === 1">
+              通过
+            </text>
+            <text v-if="orderInfo.cancelRecords?.cancelState === 2">
+              拒绝
+            </text>
+          </view>
+        </view>
+      </view>
+    </view>
+    <view class="h-220rpx" />
+    <view
+      class="fixed bottom-0 left-0 z-100 box-border h-174rpx w-full flex items-center gap-28rpx bg-#FFF px-24rpx"
+      style="border-top: 2rpx solid #EEEEEE;"
+    >
+      <view class="flex items-center gap-40rpx">
+        <view @click="router.replace({ name: 'attractions-tabbar' })">
+          <image
+            :src="`${StaticUrl}/goods-home.png`"
+            class="h44rpx w44rpx"
+          />
+          <view class="text-20rpx">
+            首页
+          </view>
+        </view>
+        <view class="relative">
+          <image
+            :src="`${StaticUrl}/goods-kf.png`"
+            class="h44rpx w44rpx"
+          />
+          <Zcontact>
+            <view class="text-20rpx">
+              客服
+            </view>
+          </Zcontact>
+        </view>
+      </view>
+      <wd-button v-if="showCancelOrRefundButton" custom-class="w-546rpx" block size="large" @click="orderPopup = true">
+        {{ isCancel ? '取消订单' : '退款申请' }}
+      </wd-button>
+    </view>
+    <Zpopup v-model="orderPopup" :showfooter="false" :zindex="9999" bg="#fff">
+      <view>
+        <view class="mt-28rpx text-center text-32rpx font-bold">
+          {{ isCancel ? '取消订单' : '退款申请' }}
+        </view>
+        <view class="px-24rpx">
+          <view class="mt-30rpx flex items-center justify-between">
+            <view class="text-28rpx">
+              退款数量
+            </view>
+            <view v-if="isCancel" class="flex items-center gap-24rpx">
+              <view
+                class="h-36rpx w-36rpx rounded-50% bg-#F0F0F0 text-center text-28rpx text-#AAAAAA font-600 line-height-[36rpx]"
+                @click="handleRefundMinus"
+              >
+                -
+              </view>
+              <view class="text-24rpx font-400">
+                {{ refundNum }}
+              </view>
+              <view
+                class="h-36rpx w-36rpx rounded-50% bg-#E8FFA7 text-center text-28rpx text-#9ED605 font-600 line-height-[36rpx]"
+                @click="handleRefundPlus"
+              >
+                +
+              </view>
+            </view>
+            <view v-else class="text-24rpx font-400">
+              {{ orderInfo?.scenicOrder?.num ?? 0 }}
+            </view>
+          </view>
+          <view class="mt-26rpx">
+            <view class="text-28rpx">
+              退款申请说明
+            </view>
+            <view class="mt-20rpx rounded-16rpx bg-#F1F1F1">
+              <textarea
+                v-model="refundReason"
+                class="h-120rpx w-full rounded-16rpx p-24rpx"
+                placeholder-style="color: #AAAAAA; font-size: 24rpx;"
+                placeholder="请仔细填写,提交后进入审核期,不可重复提交"
+              />
+            </view>
+          </view>
+        </view>
+        <view class="mt-26rpx h-2rpx w-full bg-#EEEEEE" />
+        <view class="flex items-center justify-center">
+          <wd-button custom-class="w-702rpx mt-10rpx" block size="large" @click="handleCancelOrder">
+            确认提交
+          </wd-button>
+        </view>
+        <view class="h-60rpx" />
+      </view>
+    </Zpopup>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+</style>

+ 30 - 0
src/subPack-attractions/attractionsOrderDetail/orderDetail-data.ts

@@ -0,0 +1,30 @@
+export const orderStatus = [
+  {
+    value: 0,
+    label: '待支付',
+    desc: '请在60分钟内完成支付,逾期订单自动取消',
+    icon: 'time-filled',
+    color: '#FFC130',
+  },
+  {
+    value: 70,
+    label: '已支付',
+    desc: '您的订单已付款,您可以查看或者预定更多产品',
+    icon: 'check-circle-filled',
+    color: '#52C41A',
+  },
+  {
+    value: 80,
+    label: '已完成',
+    desc: '您的订单已完成,您可以查看或者预定更多产品',
+    icon: 'check-circle-filled',
+    color: '#52C41A',
+  },
+  {
+    value: 60,
+    label: '已取消',
+    desc: '您的订单已取消,如需帮助请联系客服',
+    icon: 'close-circle-filled',
+    color: '#AAAAAA',
+  },
+]

+ 110 - 0
src/subPack-attractions/attractionsOrderPay/attractionsOrderPay.vue

@@ -0,0 +1,110 @@
+<script setup lang="ts">
+import router from '@/router'
+
+definePage({
+  name: 'attractions-order-pay',
+  islogin: false,
+  style: {
+    navigationBarTitleText: '订单支付',
+  },
+})
+const productName = ref('')
+const num = ref(0)
+const orderNo = ref('')
+onLoad((options: any) => {
+  productName.value = options.productName || ''
+  num.value = Number(options.num) || 0
+  orderNo.value = options.orderNo || ''
+})
+
+onMounted(() => {
+  getPayPreview()
+})
+
+const payPreviewInfo = ref<Api.ScenicPayPreviewVo>()
+const payMethod = ref(['1']) // 默认选中微信支付
+async function getPayPreview() {
+  const res = await Apis.attractions.payPreview({ data: { orderNumber: orderNo.value } })
+  payPreviewInfo.value = res.data
+}
+
+async function submitPay() {
+  const res = await useUserStore().handleCommonPayMent(orderNo.value)
+  if (res.payType !== 1) {
+    try {
+      await useUserStore().getWxCommonPayment(res)
+      router.replace({ name: 'attractions-order-detail', params: { orderNo: String(payPreviewInfo.value?.orderNumber), ispay: 'true' } })
+    }
+    catch {
+      await useUserStore().payError('attractions-tabbar', 'subPack-attractions/commonTab/index')
+    }
+  }
+  else {
+    router.replace({ name: 'attractions-order-detail', params: { orderNo: String(payPreviewInfo.value?.orderNumber), ispay: 'true' } })
+  }
+}
+</script>
+
+<template>
+  <view class="order-pay-page">
+    <view class="px-24rpx">
+      <view class="h-20rpx" />
+      <view class="rounded-16rpx bg-#FFF p-24rpx">
+        <view class="flex items-center justify-between gap-50rpx text-32rpx font-bold">
+          <view>{{ productName }}</view>
+          <view class="text-28rpx font-normal">
+            x{{ num }}
+          </view>
+        </view>
+        <view class="mt-24rpx h-2rpx w-full bg-#F0F0F0" />
+        <view class="mt-24rpx flex items-center justify-between">
+          <view class="text-28rpx font-bold">
+            订单总金额
+          </view>
+          <view class="text-32rpx text-#FF4A39 font-bold">
+            <text class="text-26rpx">
+              ¥
+            </text>
+            {{ payPreviewInfo?.orderTotal }}
+          </view>
+        </view>
+        <view class="mt-20rpx flex items-center justify-between text-24rpx">
+          <view>积分扣减</view>
+          <view class="text-#FF4A39 font-bold">
+            ¥{{ payPreviewInfo?.pointsDeduct }}
+          </view>
+        </view>
+        <view class="mt-20rpx flex items-center justify-between text-24rpx">
+          <view>微信支付</view>
+          <view class="text-#FF4A39 font-bold">
+            ¥{{ payPreviewInfo?.wxPayMoney }}
+          </view>
+        </view>
+      </view>
+      <view v-if="(payPreviewInfo?.wxPayMoney ?? 0) > 0" class="mt-20rpx rounded-16rpx bg-#FFF p-24rpx">
+        <view class="text-32rpx font-bold">
+          选择支付方式
+        </view>
+        <view class="mt-24rpx h-2rpx w-full bg-#F0F0F0" />
+        <view>
+          <wd-cell-group border>
+            <wd-checkbox-group v-model="payMethod" size="large">
+              <wd-cell title="微信支付" center clickable>
+                <view>
+                  <wd-checkbox model-value="1" custom-style="margin:0;" />
+                </view>
+              </wd-cell>
+            </wd-checkbox-group>
+          </wd-cell-group>
+        </view>
+      </view>
+    </view>
+    <view class="fixed bottom-0 h-174rpx w-full border-[1rpx_solid_#EEEEEE] bg-#FFF px-24rpx">
+      <wd-button custom-class="w-702rpx mt-10rpx" block size="large" @click="submitPay">
+        立即支付
+      </wd-button>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped></style>

+ 341 - 0
src/subPack-attractions/attractionsReservation/attractionsReservation.vue

@@ -0,0 +1,341 @@
+<script setup lang="ts">
+import { columns } from './reservation-data'
+import router from '@/router'
+
+definePage({
+  name: 'attractions-reservation-info',
+  islogin: true,
+  style: {
+    navigationBarTitleText: '预定信息',
+  },
+})
+
+const productNo = ref(0)
+const productName = ref('')
+const selectDate = ref('')
+const isSingle = ref()
+const price = ref(0)
+onLoad((options: any) => {
+  productNo.value = Number(options.productNo) || 0
+  productName.value = options.productName || ''
+  selectDate.value = options.selectDate || ''
+  price.value = Number(options.price)
+  isSingle.value = Number(options.isSingle)
+})
+
+// 数量选择
+const quantity = ref(1)
+function handleMinus() {
+  if (quantity.value > 1) {
+    quantity.value--
+  }
+}
+function handlePlus() {
+  quantity.value++
+}
+
+// 合计价格
+const totalPrice = computed(() => price.value * quantity.value)
+
+// 联系人信息
+const linkMan = ref('')
+const linkCreditType = ref(0)
+const linkCreditNo = ref('')
+const linkPhone = ref('')
+
+// 多游客信息列表
+interface PeopleInfo {
+  linkMan: string
+  linkCreditType: number
+  linkCreditNo: string
+}
+const peopleList = ref<PeopleInfo[]>([{ linkMan: '', linkCreditType: 0, linkCreditNo: '' }])
+
+// 监听数量变化,同步游客信息列表
+watch(quantity, (newVal) => {
+  if (isSingle.value === 1) {
+    const diff = newVal - peopleList.value.length
+    if (diff > 0) {
+      for (let i = 0; i < diff; i++) {
+        peopleList.value.push({ linkMan: '', linkCreditType: 0, linkCreditNo: '' })
+      }
+    }
+    else if (diff < 0) {
+      peopleList.value.splice(newVal)
+    }
+  }
+})
+
+const orderPopup = ref(false)
+const orderMemo = ref('')
+
+const formattedDate = computed(() => {
+  if (!selectDate.value)
+    return ''
+  const date = new Date(selectDate.value)
+  const year = date.getFullYear()
+  const month = String(date.getMonth() + 1).padStart(2, '0')
+  const day = String(date.getDate()).padStart(2, '0')
+  return `${year}年${month}月${day}日`
+})
+
+// 接口需要的日期格式 yyyy-MM-dd
+const travelDate = computed(() => {
+  if (!selectDate.value)
+    return ''
+  const date = new Date(selectDate.value)
+  const year = date.getFullYear()
+  const month = String(date.getMonth() + 1).padStart(2, '0')
+  const day = String(date.getDate()).padStart(2, '0')
+  return `${year}-${month}-${day}`
+})
+
+const { data: info } = useRequest(() =>
+  Apis.xsb.findUserPoints({}),
+)
+
+function handleLinkCreditConfirm({ value: selectedValue }: { value: number }) {
+  linkCreditType.value = selectedValue
+}
+
+function handlePeopleCreditConfirm(index: number, { value: selectedValue }: { value: number }) {
+  peopleList.value[index].linkCreditType = selectedValue
+}
+
+// 表单校验
+function validateForm(): boolean {
+  // 联系人姓名校验
+  if (!linkMan.value.trim()) {
+    useGlobalToast().show({ msg: '请输入联系人姓名' })
+    return false
+  }
+
+  // 单游客模式需要校验证件号码
+  if (isSingle.value === 0) {
+    if (!linkCreditNo.value.trim()) {
+      useGlobalToast().show({ msg: '请输入证件号码' })
+      return false
+    }
+  }
+
+  // 手机号码校验
+  if (!linkPhone.value.trim()) {
+    useGlobalToast().show({ msg: '请输入手机号码' })
+    return false
+  }
+
+  // 多游客模式校验游客信息
+  if (isSingle.value === 1) {
+    for (let i = 0; i < peopleList.value.length; i++) {
+      const people = peopleList.value[i]
+      if (!people.linkMan.trim()) {
+        useGlobalToast().show({ msg: `请输入游客${i + 1}姓名` })
+        return false
+      }
+      if (!people.linkCreditNo.trim()) {
+        useGlobalToast().show({ msg: `请输入游客${i + 1}证件号码` })
+        return false
+      }
+    }
+  }
+
+  return true
+}
+
+// 提交订单
+async function handleSubmit() {
+  // 表单校验
+  if (!validateForm())
+    return
+
+  const peoples: Api.PeopleItem[] = isSingle.value === 1
+    ? peopleList.value.map(p => ({
+        linkMan: p.linkMan,
+        linkCreditType: p.linkCreditType,
+        linkCreditNo: p.linkCreditNo,
+      }))
+    : []
+
+  const params: Api.CreateOrderRequest = {
+    productNo: productNo.value,
+    productName: productName.value,
+    num: quantity.value,
+    travelDate: travelDate.value,
+    linkMan: linkMan.value,
+    linkCreditType: isSingle.value === 0 ? linkCreditType.value : undefined,
+    linkCreditNo: isSingle.value === 0 ? linkCreditNo.value : undefined,
+    linkPhone: linkPhone.value,
+    orderMemo: orderMemo.value,
+    peoples: isSingle.value === 1 ? peoples : undefined,
+  }
+
+  try {
+    useGlobalLoading().loading({ msg: '提交中...' })
+    const res = await Apis.attractions.createOrder({ data: params })
+    useGlobalLoading().close()
+    router.push({ name: 'attractions-order-pay', params: { productName: productName.value, num: String(quantity.value), orderNo: res?.data } })
+  }
+  catch (error) {
+    useGlobalLoading().close()
+    console.error('提交订单失败', error)
+  }
+}
+</script>
+
+<template>
+  <view class="reservation-info-page">
+    <view class="px-24rpx">
+      <view class="h-20rpx" />
+      <view class="rounded-16rpx bg-#FFF p-24rpx">
+        <view class="text-32rpx font-bold">
+          {{ productName }}
+        </view>
+        <view class="mt-24rpx h-2rpx w-full bg-#F0F0F0" />
+        <view class="mt-24rpx flex items-center gap-10rpx text-28rpx">
+          <wd-icon name="calendar" size="22px" />
+          <text>选定日期:{{ formattedDate }}</text>
+        </view>
+      </view>
+      <view class="mt-20rpx flex items-center justify-between rounded-16rpx bg-#FFF p-24rpx">
+        <view class="text-28rpx">
+          选择数量
+        </view>
+        <view class="flex items-center gap-24rpx">
+          <view
+            class="h-36rpx w-36rpx rounded-50% text-center text-28rpx font-600 line-height-[36rpx]"
+            :class="quantity > 1 ? 'bg-#E8FFA7 text-#9ED605' : 'bg-#F0F0F0 text-#AAAAAA'"
+            @click="handleMinus"
+          >
+            -
+          </view>
+          <view class="text-24rpx font-400">
+            {{ quantity }}
+          </view>
+          <view
+            class="h-36rpx w-36rpx rounded-50% bg-#E8FFA7 text-center text-28rpx text-#9ED605 font-600 line-height-[36rpx]"
+            @click="handlePlus"
+          >
+            +
+          </view>
+        </view>
+      </view>
+      <!-- 单游客start -->
+      <view v-if="isSingle === 0" class="mt-20rpx rounded-16rpx bg-#FFF p-24rpx">
+        <view class="text-28rpx font-bold">
+          联系人
+        </view>
+        <view>
+          <wd-input v-model="linkMan" placeholder="请输入联系人姓名" label="联系人姓名" required />
+        </view>
+        <view class="h-2rpx w-full bg-#F0F0F0" />
+        <view>
+          <wd-picker v-model="linkCreditType" required :columns="columns" label="证件类型" @confirm="handleLinkCreditConfirm" />
+        </view>
+        <view class="h-2rpx w-full bg-#F0F0F0" />
+        <view>
+          <wd-input v-model="linkCreditNo" placeholder="请输入证件号码" label="证件号码" required />
+        </view>
+        <view class="h-2rpx w-full bg-#F0F0F0" />
+        <view>
+          <wd-input v-model="linkPhone" placeholder="请输入手机号码" label="手机号码" required />
+        </view>
+      </view>
+      <!-- 单游客end -->
+      <!-- 多游客start -->
+      <view v-if="isSingle === 1" class="mt-20rpx rounded-16rpx bg-#FFF p-24rpx">
+        <view class="text-28rpx font-bold">
+          联系人
+        </view>
+        <view>
+          <wd-input v-model="linkMan" placeholder="请输入联系人姓名" label="联系人姓名" required />
+        </view>
+        <view class="h-2rpx w-full bg-#F0F0F0" />
+        <view>
+          <wd-input v-model="linkPhone" placeholder="请输入手机号码" label="手机号码" required />
+        </view>
+      </view>
+      <view v-for="(people, index) in peopleList" :key="index" class="mt-20rpx rounded-16rpx bg-#FFF p-24rpx">
+        <view class="text-28rpx font-bold">
+          游客信息{{ index + 1 }}
+        </view>
+        <view>
+          <wd-input v-model="people.linkMan" placeholder="请输入游客姓名" :label="`游客姓名${index + 1}`" required />
+        </view>
+        <view class="h-2rpx w-full bg-#F0F0F0" />
+        <view>
+          <wd-picker v-model="people.linkCreditType" required :columns="columns" label="证件类型" @confirm="(e: { value: number }) => handlePeopleCreditConfirm(index, e)" />
+        </view>
+        <view class="h-2rpx w-full bg-#F0F0F0" />
+        <view>
+          <wd-input v-model="people.linkCreditNo" placeholder="请输入证件号码" label="证件号码" required />
+        </view>
+      </view>
+      <!-- 多游客end -->
+      <view class="mt-20rpx rounded-16rpx bg-#FFF">
+        <wd-input v-model="orderMemo" placeholder="如有特殊需要,请留言" label="订单备注" />
+      </view>
+      <view class="mt-20rpx flex items-center justify-between rounded-16rpx bg-#FFF p-24rpx">
+        <view class="text-28rpx">
+          当前可用积分
+        </view>
+        <view class="text-32rpx font-bold">
+          {{ info?.data?.availablePointsTotal || 0 }}积分
+        </view>
+      </view>
+      <view class="h-190rpx" />
+    </view>
+    <view
+      class="fixed bottom-0 left-0 z-100 box-border h-174rpx w-full flex items-center justify-between bg-#FFF px-24rpx"
+    >
+      <view class="flex items-center">
+        <view class="text-24rpx">
+          合计
+        </view>
+        <view class="text-32rpx text-#FF4A39 font-bold">
+          <text class="text-26rpx">
+            ¥
+          </text>
+          {{ totalPrice }}
+        </view>
+      </view>
+      <view class="flex items-center gap-20rpx">
+        <view class="flex items-center" @click="orderPopup = true">
+          <text class="text-24rpx">
+            明细
+          </text>
+          <wd-icon name="arrow-up" size="18px" />
+        </view>
+        <view
+          class="h-80rpx w-180rpx rounded-40rpx bg-#9ED605 text-center text-28rpx text-#FFF font-bold line-height-[80rpx]"
+          @click="handleSubmit"
+        >
+          提交
+        </view>
+      </view>
+    </view>
+    <Zpopup v-model="orderPopup" :zindex="99" bg="#fff">
+      <view class="p-24rpx">
+        <view class="text-center text-28rpx font-bold">
+          订单明细
+        </view>
+        <view class="mt-24rpx h-2rpx w-full bg-#F0F0F0" />
+        <view class="mt24rpx text-28rpx">
+          <view class="flex items-center gap-56rpx">
+            <view>{{ productName }}</view>
+            <view>
+              <text class="text-#FF4A39 font-bold">
+                ¥{{ price }}
+              </text>
+              <text>×{{ quantity }}</text>
+            </view>
+          </view>
+          <view class="mt-20rpx">
+            {{ formattedDate }}
+          </view>
+        </view>
+      </view>
+    </Zpopup>
+  </view>
+</template>
+
+<style lang="scss" scoped></style>

+ 24 - 0
src/subPack-attractions/attractionsReservation/reservation-data.ts

@@ -0,0 +1,24 @@
+export const columns = ref([
+  { label: '身份证', value: 0 },
+  { label: '学生证', value: 1 },
+  { label: '军官证', value: 2 },
+  { label: '护照', value: 3 },
+  { label: '户口本(儿童请选择此项)', value: 4 },
+  { label: '港澳通行证', value: 5 },
+  { label: '台湾居民来往大陆通行证', value: 6 },
+  { label: '台湾通行证', value: 7 },
+  { label: '入台证', value: 8 },
+  { label: '香港居民往来内地通行证', value: 9 },
+  { label: '警官证', value: 10 },
+  { label: '驾驶证', value: 11 },
+  { label: '海员证', value: 12 },
+  { label: '外国人在中国永久居留证', value: 13 },
+  { label: '澳门居民往来内地通行证', value: 14 },
+  { label: '港澳居民来往内地通行证', value: 15 },
+  { label: '港澳台居民来往内地通行证', value: 16 },
+  { label: '港澳台居民居住证', value: 17 },
+  { label: '中华人民共和国旅行证', value: 18 },
+  { label: '回乡证', value: 19 },
+  { label: '台胞证', value: 20 },
+  { label: '香港身份证', value: 21 },
+])

+ 128 - 0
src/subPack-attractions/commonTab/components/homeList.vue

@@ -0,0 +1,128 @@
+<script setup lang="ts">
+import router from '@/router'
+import { StaticUrl } from '@/config'
+
+const globalToast = useGlobalToast()
+const { statusBarHeight, MenuButtonHeight, opcity } = storeToRefs(useSysStore())
+/**
+ * 获取景区列表
+ */
+const loading = ref(true)
+const productName = ref('')
+const { data: attractionsList, isLastPage, page, refresh, error, reload } = usePagination((pageNum, pageSize) =>
+  Apis.attractions.findAppByPage({ data: { pageNum, pageSize, productName: productName.value } }), {
+  data: (resp) => {
+    return resp.data?.list
+  },
+  initialData: [],
+  initialPage: 1,
+  initialPageSize: 10,
+  append: true,
+  immediate: true,
+}).onSuccess(() => {
+  loading.value = false
+})
+const state = computed(() => {
+  return error.value ? 'error' : !isLastPage.value ? 'loading' : 'finished'
+})
+onReachBottom(() => {
+  if (!isLastPage.value) {
+    page.value++
+  }
+})
+onMounted(() => {
+  opcity.value = 0
+})
+
+function handleSearch() {
+  if (productName.value === '') {
+    return globalToast.warning('搜索内容不能为空')
+  }
+  page.value = 1
+  refresh()
+}
+
+function handleClear() {
+  productName.value = ''
+  page.value = 1
+  refresh()
+}
+</script>
+
+<template>
+  <view class="attractions-home-page">
+    <wd-navbar
+      title="景区门票"
+      :custom-style="`background-color: rgba(226, 255, 145, ${opcity})`"
+      :bordered="false"
+      :z-index="9999"
+      safe-area-inset-top
+      left-arrow
+      fixed
+      @click-left="router.back()"
+    />
+
+    <view class="relative h-624rpx w-full">
+      <image class="h-full w-full" :src="`${StaticUrl}/attractions-home-bg.png`" />
+      <view :style="{ height: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 12}px` }" />
+
+      <view class="absolute left-24rpx right-24rpx top-198rpx">
+        <view class="h-60rpx w-full flex items-center justify-between rounded-40rpx bg-white pr-6rpx">
+          <view class="flex flex-1 items-center pb-14rpx pl-24rpx pt-16rpx">
+            <wd-icon name="search" size="14" color="#ccc" />
+            <input v-model="productName" type="text" placeholder="请输入景区名称" class="ml-12rpx h-30rpx w-full flex-1 text-24rpx outline-none" placeholder-style="color:#AAA" @confirm="handleSearch">
+            <view v-if="productName" class="w-80rpx flex items-center justify-center" @click="handleClear">
+              <wd-icon name="close" size="14" color="#ccc" class="mr-12rpx" />
+            </view>
+          </view>
+          <view
+            class="h-50rpx w-96rpx flex items-center justify-center rounded-26rpx bg-[var(--them-color)] text-24rpx text-white font-semibold"
+            @click="handleSearch"
+          >
+            搜索
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <view class="relative px-24rpx">
+      <view>
+        <scroll-view scroll-y type="custom">
+          <wd-skeleton theme="paragraph" animation="gradient" :loading="loading" :row-col="[{ height: '100px', width: '100%' }, { height: '100px', width: '100%' }, { height: '100px', width: '100%' }]">
+            <view v-for="item in attractionsList" :key="item.productNo" class="mb-20rpx flex items-center gap-24rpx rounded-16rpx bg-#FFF p-24rpx" @click="router.push({ name: 'attractions-detail', params: { productNo: String(item.productNo) } })">
+              <image
+                class="h-160rpx w-160rpx rounded-16rpx"
+                :src="item.img"
+              />
+              <view class="flex-1">
+                <view class="text-32rpx font-bold">
+                  {{ item.productName }}
+                </view>
+                <view class="mt-24rpx flex items-center justify-between">
+                  <view>
+                    <text class="text-26rpx text-#FF4D3A">
+                      ¥
+                    </text>
+                    <text class="text-36rpx text-#FF4D3A font-bold">
+                      {{ item.salePrice }}
+                    </text>
+                    <text class="text-24rpx text-#AAA">
+                      起
+                    </text>
+                  </view>
+                  <image
+                    class="h-48rpx w-116rpx"
+                    :src="`${StaticUrl}/attractions-home-btn.png`"
+                  />
+                </view>
+              </view>
+            </view>
+          </wd-skeleton>
+        </scroll-view>
+        <wd-loadmore :state="state" :loading-props="{ color: '#9ED605', size: 20 }" @reload="reload" />
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped></style>

+ 77 - 0
src/subPack-attractions/commonTab/components/orderList.vue

@@ -0,0 +1,77 @@
+<script setup lang="ts">
+import { tabsList } from '../order-data'
+import attractionsList from '@/subPack-smqjh/components/attractions-orderList/attractions-orderList.vue?async'
+import { createGlobalLoadingMiddleware } from '@/api/core/middleware'
+
+const tab = ref<number>(0)
+const { statusBarHeight, MenuButtonHeight } = storeToRefs(useSysStore())
+
+// 状态映射:tab索引 -> 接口status
+const statusMap: Record<number, string> = {
+  0: 'all', // 全部
+  1: 'paddingPay', // 待支付
+  2: 'ing', // 已支付
+  3: 'cancel', // 已取消
+  4: 'completed', // 已完成
+}
+
+const { data: orderList, isLastPage, page, error, refresh, reload } = usePagination((pageNum, pageSize) => Apis.xsb.orderList({ data: {
+  businessType: 'JDMP',
+  pageNum,
+  pageSize,
+  orderStatus: statusMap[tab.value],
+} }), {
+  immediate: true,
+  pageNum: 1,
+  pageSize: 10,
+  initialData: [],
+  data: res => res.data?.list,
+  append: true,
+  middleware: createGlobalLoadingMiddleware(),
+})
+onShow(() => {
+  refresh()
+})
+function handleClick(e: { index: number }) {
+  tab.value = e.index
+  orderList.value = []
+  refresh()
+}
+
+onReachBottom(() => {
+  if (!isLastPage.value) {
+    page.value++
+    refresh()
+  }
+})
+const state = computed(() => {
+  return error.value ? 'error' : !isLastPage.value ? 'loading' : 'finished'
+})
+</script>
+
+<template>
+  <view class="attractions-order-page" style="min-height: 100vh; background-color: #f6f6f6;">
+    <wd-navbar
+      title="订单列表" custom-style="background-color:#FFF" :bordered="false" :z-index="99"
+      safe-area-inset-top fixed
+    />
+    <view :style="{ paddingTop: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 12}px` }" />
+    <wd-tabs v-model="tab" :offset-top="statusBarHeight + MenuButtonHeight + 12" sticky custom-style="background-color: transparent;" @click="handleClick">
+      <block v-for="(item, index) in tabsList" :key="index">
+        <wd-tab :title="item">
+          <view class="box-border w-full overflow-hidden px24rpx">
+            <view class="h-20rpx" />
+            <StatusTip v-if="!orderList.length" tip="暂无内容" />
+            <view v-for="order in orderList" :key="order.orderNumber">
+              <attractionsList :order="order" />
+            </view>
+            <wd-loadmore :state="state" :loading-props="{ color: '#9ED605', size: 20 }" @reload="reload" />
+            <view class="h-40rpx" />
+          </view>
+        </wd-tab>
+      </block>
+    </wd-tabs>
+  </view>
+</template>
+
+<style lang="scss" scoped></style>

+ 54 - 0
src/subPack-attractions/commonTab/index.vue

@@ -0,0 +1,54 @@
+<script setup lang="ts">
+import homeList from './components/homeList.vue'
+import orderList from './components/orderList.vue'
+import { StaticUrl } from '@/config'
+
+const tabbar = ref(0)
+const { opcity } = storeToRefs(useSysStore())
+
+definePage({
+  name: 'attractions-tabbar',
+  islogin: false,
+  style: {
+    navigationBarTitleText: '',
+    navigationStyle: 'custom',
+  },
+})
+onLoad((options: any) => {
+  if (options.tabbar) {
+    tabbar.value = Number(options.tabbar)
+  }
+})
+// 页面级滚动监听 - 必须在页面组件中才能生效
+onPageScroll((e) => {
+  if (tabbar.value === 0) {
+    const calculatedOpacity = e.scrollTop / 100
+    opcity.value = Math.min(1, Math.max(0.1, calculatedOpacity))
+  }
+})
+</script>
+
+<template>
+  <view class="">
+    <home-list v-if="tabbar === 0" />
+    <order-list v-if="tabbar === 1" />
+    <view class="">
+      <wd-tabbar v-model="tabbar" safe-area-inset-bottom placeholder fixed :bordered="false" :z-index="99999">
+        <wd-tabbar-item title="列表" icon="goods">
+          <template #icon>
+            <image v-if="tabbar === 0" class="h-40rpx w-40rpx" :src="`${StaticUrl}/attractions-selHome-tabbar.png`" />
+            <image v-else class="h-40rpx w-40rpx" :src="`${StaticUrl}/attractions-home-tabbar.png`" />
+          </template>
+        </wd-tabbar-item>
+        <wd-tabbar-item title="订单记录" icon="list">
+          <template #icon>
+            <image v-if="tabbar === 1" class="h-40rpx w-40rpx" :src="`${StaticUrl}/attractions-selOrder-tabbar.png`" />
+            <image v-else class="h-40rpx w-40rpx" :src="`${StaticUrl}/attractions-order-tabbar.png`" />
+          </template>
+        </wd-tabbar-item>
+      </wd-tabbar>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped></style>

+ 1 - 0
src/subPack-attractions/commonTab/order-data.ts

@@ -0,0 +1 @@
+export const tabsList = ['全部', '待支付', '已支付', '已取消', '已完成']

+ 237 - 0
src/subPack-attractions/components/DatePicker.vue

@@ -0,0 +1,237 @@
+<script setup lang="ts">
+/**
+ * 日期选择器组件
+ * @description 支持年月切换、日期选择、价格/库存状态展示
+ */
+
+interface DayItem {
+  day: number
+  status?: string
+  price?: number
+  selected?: boolean
+  disabled?: boolean
+}
+
+interface Props {
+  /** 当前选中的日期 */
+  modelValue?: Date
+  /** 标题 */
+  title?: string
+  /** 日期数据 */
+  dayData?: DayItem[]
+  /** 是否显示星期标题 */
+  showWeekHeader?: boolean
+  /** 自定义类名 */
+  customClass?: string
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  modelValue: () => new Date(),
+  title: '选择日期',
+  dayData: () => [],
+  showWeekHeader: true,
+  customClass: '',
+})
+
+const emit = defineEmits<{
+  /** 选中日期变化 */
+  (e: 'update:modelValue', value: Date): void
+  /** 选中日期 */
+  (e: 'select', value: { date: Date, day: number, item?: DayItem }): void
+  /** 月份变化 */
+  (e: 'monthChange', value: { year: number, month: number }): void
+}>()
+
+// 星期标题
+const weekDays = ['一', '二', '三', '四', '五', '六', '日']
+
+// 当前显示的日期
+const currentDate = ref(new Date(props.modelValue))
+
+// 当前年月显示文本
+const currentMonthText = computed(() => {
+  return `${currentDate.value.getFullYear()}年${currentDate.value.getMonth() + 1}月`
+})
+
+// 当前年月值
+const currentYear = computed(() => currentDate.value.getFullYear())
+const currentMonth = computed(() => currentDate.value.getMonth())
+
+// 计算当前月日历数据
+const calendarDays = computed(() => {
+  const year = currentYear.value
+  const month = currentMonth.value
+
+  // 获取当月第一天是星期几 (0=周日, 1=周一...)
+  const firstDayOfMonth = new Date(year, month, 1).getDay()
+  // 转换为周一开头 (0=周一, 6=周日)
+  const firstDayIndex = firstDayOfMonth === 0 ? 6 : firstDayOfMonth - 1
+
+  // 获取当月天数
+  const daysInMonth = new Date(year, month + 1, 0).getDate()
+
+  // 获取上月天数(用于填充前置空白)
+  const daysInPrevMonth = new Date(year, month, 0).getDate()
+
+  const days: Array<{ day: number, isCurrentMonth: boolean, item?: DayItem, date: Date }> = []
+
+  // 填充上月日期(灰色显示)
+  for (let i = firstDayIndex - 1; i >= 0; i--) {
+    const day = daysInPrevMonth - i
+    days.push({
+      day,
+      isCurrentMonth: false,
+      date: new Date(year, month - 1, day),
+    })
+  }
+
+  // 填充当月日期
+  for (let day = 1; day <= daysInMonth; day++) {
+    // 查找对应的日期数据
+    const dayItem = props.dayData.find(d => d.day === day)
+    const date = new Date(year, month, day)
+
+    // 判断是否选中
+    const isSelected = props.modelValue
+      && date.getFullYear() === props.modelValue.getFullYear()
+      && date.getMonth() === props.modelValue.getMonth()
+      && date.getDate() === props.modelValue.getDate()
+
+    days.push({
+      day,
+      isCurrentMonth: true,
+      item: dayItem ? { ...dayItem, selected: isSelected } : undefined,
+      date,
+    })
+  }
+
+  // 填充下月日期(补全到6行或5行)
+  const remainingCells = 42 - days.length // 6行 x 7列 = 42
+  for (let day = 1; day <= remainingCells; day++) {
+    days.push({
+      day,
+      isCurrentMonth: false,
+      date: new Date(year, month + 1, day),
+    })
+  }
+
+  return days
+})
+
+// 切换到上月
+function prevMonth() {
+  const newDate = new Date(currentDate.value)
+  newDate.setMonth(newDate.getMonth() - 1)
+  currentDate.value = newDate
+  emit('monthChange', { year: newDate.getFullYear(), month: newDate.getMonth() + 1 })
+}
+
+// 切换到下月
+function nextMonth() {
+  const newDate = new Date(currentDate.value)
+  newDate.setMonth(newDate.getMonth() + 1)
+  currentDate.value = newDate
+  emit('monthChange', { year: newDate.getFullYear(), month: newDate.getMonth() + 1 })
+}
+
+// 选择日期
+function selectDay(dayInfo: typeof calendarDays.value[0]) {
+  if (!dayInfo.isCurrentMonth)
+    return
+
+  const newDate = new Date(currentDate.value)
+  newDate.setDate(dayInfo.day)
+
+  emit('update:modelValue', newDate)
+  emit('select', {
+    date: newDate,
+    day: dayInfo.day,
+    item: dayInfo.item,
+  })
+}
+
+// 监听外部modelValue变化
+watch(() => props.modelValue, (newVal) => {
+  if (newVal) {
+    currentDate.value = new Date(newVal)
+  }
+})
+</script>
+
+<template>
+  <view class="rounded-16rpx bg-#F6F6F6 p-20rpx" :class="customClass">
+    <view class="text-32rpx font-semibold">
+      {{ title }}
+    </view>
+
+    <!-- 年月切换 -->
+    <view class="mt20rpx flex items-center justify-between">
+      <view class="h60rpx w60rpx flex items-center justify-center" @click="prevMonth">
+        <wd-icon name="arrow-left" size="20px" color="#999" />
+      </view>
+      <view class="rounded-16rpx bg-#E6E6E6 px12rpx px32rpx py12rpx py8rpx text-28rpx">
+        {{ currentMonthText }}
+      </view>
+      <view class="h60rpx w60rpx flex items-center justify-center" @click="nextMonth">
+        <wd-icon name="arrow-right" size="20px" color="#999" />
+      </view>
+    </view>
+
+    <!-- 星期标题 -->
+    <view v-if="showWeekHeader" class="mt20rpx flex items-center justify-between px12rpx">
+      <view
+        v-for="day in weekDays"
+        :key="day"
+        class="h60rpx w60rpx flex items-center justify-center text-28rpx"
+        :class="day === '六' || day === '日' ? 'text-#52c41a' : 'text-#333'"
+      >
+        {{ day }}
+      </view>
+    </view>
+
+    <!-- 日期网格 -->
+    <view class="mt12rpx flex flex-wrap">
+      <view
+        v-for="(item, index) in calendarDays"
+        :key="index"
+        class="mb16rpx h90rpx w-14.28% flex flex-col items-center justify-center"
+        @click="selectDay(item)"
+      >
+        <view
+          class="h100rpx w76rpx flex flex-col items-center justify-center rounded-16rpx"
+          :class="[
+            item.item?.selected ? 'bg-#E2FF91' : '',
+            item.item?.status === '售罄' || item.item?.disabled ? 'bg-#F5F5F5' : '',
+            !item.isCurrentMonth ? 'opacity-30' : '',
+          ]"
+        >
+          <text
+            v-if="item.item?.status && item.isCurrentMonth"
+            class="mt4rpx text-20rpx"
+            :class="item.item.status === '充足' ? 'text-#52c41a' : 'text-#999'"
+          >
+            {{ item.item.status }}
+          </text>
+          <text
+            class="text-28rpx"
+            :class="[
+              item.item?.selected ? 'text-#333 font-semibold' : 'text-#333',
+              item.item?.status === '售罄' || item.item?.disabled ? 'text-#999' : '',
+              !item.isCurrentMonth ? 'text-#999' : '',
+            ]"
+          >
+            {{ item.day }}
+          </text>
+          <text
+            v-if="item.item?.price && item.isCurrentMonth && item.item.status !== '售罄'"
+            class="mt4rpx text-20rpx text-#FF4D3A"
+          >
+            ¥{{ item.item.price }}
+          </text>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped></style>

+ 50 - 0
src/subPack-attractions/components/orderDetailStatus.vue

@@ -0,0 +1,50 @@
+<script setup lang="ts">
+import { orderStatus } from '../attractionsOrderDetail/orderDetail-data'
+import router from '@/router'
+
+const props = defineProps<{
+  status: number
+}>()
+
+const emit = defineEmits<{
+  (e: 'pay'): void
+  (e: 'rebook'): void
+}>()
+
+const currentStatus = computed(() => {
+  return orderStatus.find(item => item.value === props.status) || orderStatus[0]
+})
+</script>
+
+<template>
+  <view class="text-center">
+    <view class="flex items-center justify-center gap-10rpx">
+      <wd-icon :name="currentStatus.icon" size="20px" :color="currentStatus.color" />
+      <text class="text-32rpx font-bold" :style="{ color: currentStatus.color }">
+        {{ currentStatus.label }}
+      </text>
+    </view>
+    <view class="mt-20rpx text-24rpx text-#AAA">
+      {{ currentStatus.desc }}
+    </view>
+    <view v-if="props.status === 0" class="mt-20rpx">
+      <wd-button class="w-456rpx" type="primary" @click="emit('pay')">
+        去支付
+      </wd-button>
+    </view>
+    <view v-if="props.status === 60" class="mt-20rpx">
+      <wd-button class="w-456rpx" type="primary" @click="emit('rebook')">
+        重新预订
+      </wd-button>
+    </view>
+    <view class="mt-24rpx flex items-center justify-center gap-10rpx" @click="router.push({ name: 'attractions-tabbar', params: { tabbar: '1' } })">
+      <text class="text-28rpx">
+        查看订单列表
+      </text>
+      <wd-icon name="chevron-right" size="16px" />
+    </view>
+    <view class="my-24rpx h-2rpx w-full bg-#F0F0F0" />
+  </view>
+</template>
+
+<style lang="scss" scoped></style>

+ 74 - 0
src/subPack-charge/chargeAddPlate/chargeAddPlate.vue

@@ -0,0 +1,74 @@
+<script setup lang="ts">
+import { ref } from 'vue'
+import addPlate from '@/subPack-charge/components/plate/index.vue'
+import router from '@/router'
+
+const globalToast = useGlobalToast()
+definePage({
+  name: 'charge-add-plate',
+  islogin: false,
+  style: {
+    navigationBarTitleText: '添加车牌',
+  },
+})
+
+const plateNumber = ref<(string | number)[]>(Array.from({ length: 8 }, () => ''))
+
+const { userInfo } = storeToRefs(useUserStore())
+
+const plateRef = ref<InstanceType<typeof addPlate> | null>(null)
+
+function handlePlateChange(value: (string | number)[]) {
+  plateNumber.value = value
+}
+
+// 保存
+async function handleSave() {
+  const result = plateRef.value?.validatePlateNumber()
+  if (!result?.valid) {
+    globalToast.warning(result?.message || '请输入正确的车牌号')
+    return
+  }
+
+  const plateString = plateRef.value?.getPlateString()?.replace(/·/g, '') || ''
+
+  await Apis.charge.addVehicle({
+    data: {
+      licensePlate: plateString,
+      userId: userInfo.value.id!,
+    },
+  })
+  setTimeout(() => {
+    router.back()
+  }, 500)
+}
+</script>
+
+<template>
+  <view class="box-border px24rpx">
+    <view class="h-20rpx" />
+    <view class="rounded-16rpx bg-#FFF p-20rpx shadow-[0_4rpx_20rpx_0_rgba(101,108,106,0.1)]">
+      <view class="mb-24rpx">
+        <text class="text-32rpx font-600">
+          车牌号码
+        </text>
+        <text class="text-#9ED605">
+          *
+        </text>
+      </view>
+      <addPlate
+        ref="plateRef"
+        :plate-number="plateNumber"
+        @my-plate-change="handlePlateChange"
+      />
+      <view
+        class="mt-24rpx h-80rpx w-664rpx rounded-16rpx bg-[linear-gradient(90deg,#DBFC81_0%,#9ED605_100%)] text-center text-28rpx font-800 line-height-[80rpx] shadow-[inset_0rpx_6rpx_20rpx_2rpx_#FFFFFF]"
+        @click="handleSave"
+      >
+        保存
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped></style>

+ 97 - 0
src/subPack-charge/chargeBuyaTicketList/chargeBuyaTicketList.vue

@@ -0,0 +1,97 @@
+<script setup lang="ts">
+import { createGlobalLoadingMiddleware } from '@/api/core/middleware'
+
+definePage({
+  name: 'charge-buy-a-ticket-list',
+  islogin: false,
+  style: {
+    navigationBarTitleText: '购券记录',
+  },
+})
+
+/**
+ * 获取购券记录列表
+ */
+const { data: recordList, isLastPage, page } = usePagination((pageNum, pageSize) =>
+  Apis.charge.getPurchaseRecordPage({ data: { pageNum, pageSize } }), {
+  data: (resp) => {
+    return resp.data?.list
+  },
+  initialData: [],
+  initialPage: 1,
+  initialPageSize: 10,
+  append: true,
+  immediate: true,
+  middleware: createGlobalLoadingMiddleware(),
+})
+
+onReachBottom(() => {
+  if (!isLastPage.value) {
+    page.value++
+  }
+})
+
+/**
+ * 获取状态文本
+ */
+function getStatusText(recordStatus?: number) {
+  switch (recordStatus) {
+    case 1:
+      return '已取消'
+    case 2:
+      return '已到账'
+    case 3:
+      return '已退款'
+    default:
+      return '未知'
+  }
+}
+
+/**
+ * 获取状态样式
+ */
+function getStatusClass(recordStatus?: number) {
+  switch (recordStatus) {
+    case 1:
+      return 'bg-#AAAAAA text-#666666'
+    case 2:
+      return 'bg-#9ED605 text-#FFFFFF'
+    case 3:
+      return 'bg-#FF9500 text-#FFFFFF'
+    default:
+      return 'bg-#AAAAAA text-#666666'
+  }
+}
+</script>
+
+<template>
+  <view class="min-h-screen bg-#F5F7FB px-24rpx">
+    <view class="h-10rpx" />
+    <StatusTip v-if="!recordList.length" tip="暂无内容" />
+    <view v-for="item in recordList" :key="item.recordNo" class="mt-20rpx bg-#FFFFFF px-24rpx py-28rpx">
+      <view class="relative flex items-center justify-between">
+        <view class="text-32rpx font-bold">
+          {{ item.couponAmount }}元充电费抵扣券
+        </view>
+        <view class="absolute right-[-24rpx] rounded-[30rpx_0rpx_0rpx_4rpx] px-14rpx py-8rpx text-28rpx text-#FFF" :class="getStatusClass(item.recordStatus)">
+          • {{ getStatusText(item.recordStatus) }}
+        </view>
+      </view>
+      <view class="mt-28rpx h-2rpx w-full bg-#F0F0F0" />
+      <view class="mt-28rpx">
+        <view class="text-24rpx text-#AAAAAA">
+          订单编号:{{ item.recordNo }}
+        </view>
+        <view class="mt-20rpx text-24rpx text-#AAAAAA">
+          购券时间:{{ item.createTime }}
+        </view>
+        <view class="mt-20rpx text-24rpx text-#AAAAAA">
+          实付金额:{{ item.couponAmount }}元
+        </view>
+      </view>
+    </view>
+    <view class="h-20rpx" />
+  </view>
+</template>
+
+<style lang="scss" scoped></style>

+ 19 - 3
src/subPack-charge/chargeOrderDetail/chargeOrderDetail.vue

@@ -158,10 +158,26 @@ async function getOrderDetail() {
         </view> -->
         <view class="mt-28rpx flex items-center justify-between">
           <view class="text-28rpx font-500">
-            实际结算费用
+            订单金额
           </view>
           <view class="text-28rpx text-#F44033">
-            {{ chargeOrderDetail?.realCost }}元
+            {{ chargeOrderDetail?.actualTotal || '--' }}元
+          </view>
+        </view>
+        <view class="mt-28rpx flex items-center justify-between">
+          <view class="text-24rpx">
+            积分扣减
+          </view>
+          <view class="text-24rpx text-#F44033">
+            {{ chargeOrderDetail ? (chargeOrderDetail.actualTotal || 0) - (chargeOrderDetail.platformVolume || 0) : '--' }}元
+          </view>
+        </view>
+        <view class="mt-28rpx flex items-center justify-between">
+          <view class="text-24rpx">
+            平台券
+          </view>
+          <view class="text-24rpx text-#F44033">
+            {{ chargeOrderDetail?.platformVolume || '--' }}元
           </view>
         </view>
       </view>
@@ -170,7 +186,7 @@ async function getOrderDetail() {
           !
         </view>
         <view class="text-24rpx">
-          本次充电费用已从您的“账户余额”中抵扣结算
+          本次充电费用已通过您的积分与市民券抵扣结算
         </view>
       </view>
     </view>

+ 106 - 0
src/subPack-charge/chargePlateList/chargePlateList.vue

@@ -0,0 +1,106 @@
+<script setup lang="ts">
+import router from '@/router'
+import { createGlobalLoadingMiddleware } from '@/api/core/middleware'
+
+definePage({
+  name: 'charge-plate-list',
+  islogin: true,
+  style: {
+    navigationBarTitleText: '车牌管理',
+  },
+})
+
+const { data: plateList, refresh } = usePagination(() => Apis.charge.vehicleList({}), {
+  immediate: false,
+  pageNum: 1,
+  pageSize: 10,
+  initialData: [],
+  data: res => res.data,
+  append: true,
+  middleware: createGlobalLoadingMiddleware(),
+})
+
+onShow(() => {
+  refresh()
+})
+
+// 设置默认车辆
+async function handleSetDefault(id: number) {
+  useGlobalMessage().confirm({
+    title: '提示',
+    msg: '确定要设置该车辆为默认车辆吗?',
+    success: async () => {
+      await Apis.charge.setDefault({
+        pathParams: { id: String(id) },
+      })
+      await refresh()
+    },
+  })
+}
+
+// 删除车辆
+function handleDelete(id: number) {
+  useGlobalMessage().confirm({
+    title: '提示',
+    msg: '确定要删除该车辆吗?',
+    success: async () => {
+      await Apis.charge.deleteVehicle({
+        pathParams: { id: String(id) },
+      })
+      await refresh()
+    },
+  })
+}
+</script>
+
+<template>
+  <view class="box-border px24rpx">
+    <view>
+      <view class="h-20rpx" />
+      <view v-for="item in plateList" :key="item.id" class="relative mb-20rpx h-180rpx w-full flex items-center justify-between rounded-16rpx bg-[#9ED605]/30">
+        <view class="ml-20rpx text-32rpx font-bold">
+          {{ item.licensePlate }}
+        </view>
+        <view class="mr-20rpx text-26rpx text-#666666">
+          <view v-if="item.isDefault === 1" class="absolute right-0 top-0 rounded-[0rpx_16rpx_0rpx_16rpx] bg-#9ED605 px-24rpx py-8rpx text-#FFF">
+            默认
+          </view>
+          <view v-else @click="handleSetDefault(item.id!)">
+            设为默认
+          </view>
+          <view class="mt-20rpx" @click="handleDelete(item.id!)">
+            <text>🗑 删除</text>
+          </view>
+        </view>
+      </view>
+      <StatusTip v-if="!plateList.length" tip="暂未绑定车辆" />
+    </view>
+    <view class="mt-100rpx">
+      <view class="text-32rpx font-bold">
+        规则说明
+      </view>
+      <view class="mt-16rpx text-28rpx text-#666666">
+        <view class="mt-10rpx">
+          1.先绑定车牌再开始充电,才能享受充电停车费减免,充电过程中绑定车牌的无法减免停车费;
+        </view>
+        <view class="mt-10rpx">
+          2.绑定车牌后,将按订单充电时长+30分钟离场时间进行减免停车费(例如:充电时长60分钟,系统自动延长30分钟离场时间,即离场时减免90分钟停车费);
+        </view>
+        <view class="mt-10rpx">
+          3.绑定多个车牌时,请在充电开始前,确认充电车辆已设为当前默认充电车辆后再开始充电,否则无法进行减免充电停车费;
+        </view>
+        <view class="mt-10rpx">
+          4.车牌绑定未按正确操作流程或车牌未对应现场充电车辆导致无法减免停车费,因此产生的一切损失与本平台无关。
+        </view>
+      </view>
+    </view>
+    <view
+      class="fixed bottom-66rpx left-24rpx h-100rpx w-702rpx rounded-16rpx bg-[linear-gradient(90deg,#DBFC81_0%,#9ED605_100%)] text-center text-28rpx font-800 line-height-[100rpx] shadow-[inset_0rpx_6rpx_20rpx_2rpx_#FFFFFF]"
+      @click="router.push({ name: 'charge-add-plate' })"
+    >
+      添加车辆
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped></style>

+ 39 - 1
src/subPack-charge/chargeStart/chargeStart.vue

@@ -16,7 +16,9 @@ const connectorCode = ref()
 onLoad((options: any) => {
   connectorCode.value = options.connectorCode
 })
-
+onShow(() => {
+  queryVehicleList()
+})
 onMounted(() => {
   opcity.value = 0
   getConnectorDetail()
@@ -33,6 +35,25 @@ async function getConnectorDetail() {
   connectorDetailInfo.value = res.data
 }
 
+// 获取车辆列表判断是否提示用户添加车辆
+const defaultPlateNumber = ref('')
+const vehicleList = ref<any[]>([])
+async function queryVehicleList() {
+  const res = await Apis.charge.vehicleList({})
+  vehicleList.value = res.data || []
+  const defaultVehicle = res.data.find(item => item.isDefault === 1) || res.data[0]
+  defaultPlateNumber.value = defaultVehicle?.licensePlate || ''
+  if (res.data.length === 0) {
+    useGlobalMessage().confirm({
+      title: '提示',
+      msg: '您还未绑定车牌,绑定车牌可享免费停车',
+      success: () => {
+        router.push({ name: 'charge-plate-list' })
+      },
+    })
+  }
+}
+
 const fromData = ref<Api.invokeChargeList>({
   equipmentId: 0,
   stationId: 0,
@@ -40,14 +61,31 @@ const fromData = ref<Api.invokeChargeList>({
   powerStationName: '',
   consigneeName: userInfo.value?.nickName,
   consigneeMobile: userInfo.value?.mobile,
+  plateNum: '',
 })
 async function launchCharge() {
   // router.push({ name: 'chargeing' })
   // return
+  if (vehicleList.value.length > 1) {
+    useGlobalMessage().confirm({
+      title: '提示',
+      msg: `系统默认充电车牌"${defaultPlateNumber.value}",请确认当前车牌后再进行充电。`,
+      success: () => {
+        beginLaunchCharge()
+      },
+    })
+  }
+  else {
+    beginLaunchCharge()
+  }
+}
+
+async function beginLaunchCharge() {
   fromData.value.equipmentId = connectorDetailInfo.value?.equipmentId
   fromData.value.stationId = connectorDetailInfo.value?.stationId
   fromData.value.connectorId = connectorDetailInfo.value?.connectorCode
   fromData.value.powerStationName = connectorDetailInfo.value?.stationName
+  fromData.value.plateNum = defaultPlateNumber.value
   const res = await Apis.charge.invokeCharge({ data: fromData.value })
   if (res.code === '00000') {
     router.push({ name: 'chargeing', params: { orderNo: res.data } })

+ 198 - 0
src/subPack-charge/chargeVoucher/chargeVoucher.vue

@@ -0,0 +1,198 @@
+<script setup lang="ts">
+import router from '@/router'
+import { StaticUrl } from '@/config'
+
+const { userInfo } = storeToRefs(useUserStore())
+definePage({
+  name: 'charge-voucher',
+  islogin: true,
+  style: {
+    navigationBarTitleText: '',
+    navigationStyle: 'custom',
+  },
+})
+const { statusBarHeight, MenuButtonHeight, opcity } = storeToRefs(useSysStore())
+const couponBalance = ref()
+onLoad((options: any) => {
+  console.log(options, '路由参数')
+  couponBalance.value = options.couponBalance
+})
+onMounted(() => {
+  opcity.value = 0
+  getRechargeLevels()
+})
+onPageScroll((e) => {
+  const calculatedOpacity = e.scrollTop / 100
+  opcity.value = Math.min(1, Math.max(0.1, calculatedOpacity))
+})
+
+const selectIndex = ref<number>()
+
+/**
+ * 获取充值挡位
+ */
+const rechargeLevels = ref<Api.RechargeLevel[]>([])
+async function getRechargeLevels() {
+  const res = await Apis.charge.getReChargeLevel({})
+  rechargeLevels.value = res.data
+}
+function selectVoucher(index: number) {
+  selectIndex.value = index
+  console.log('Selected voucher index:', index)
+}
+
+// 安全获取充值档位
+function getSelectedLevel(): Api.RechargeLevel | undefined {
+  if (selectIndex.value === undefined || !rechargeLevels.value)
+    return undefined
+  return rechargeLevels.value[selectIndex.value]
+}
+
+/**
+ * 提交支付
+ */
+async function submitPay() {
+  if (selectIndex.value === undefined) {
+    return useGlobalMessage().alert('请选择充值金额')
+  }
+  const selectedLevel = getSelectedLevel()
+  if (!selectedLevel) {
+    return
+  }
+  try {
+    await uni.showLoading({ title: '下单中...', mask: true })
+    const orderRes = await Apis.charge.addPurchaseRecord({
+      data: {
+        couponAmount: selectedLevel.money,
+        consigneeName: userInfo.value?.nickName || '',
+        consigneeMobile: userInfo.value?.mobile || '',
+      },
+    })
+    const orderNumber = orderRes.data
+    if (!orderNumber) {
+      uni.hideLoading()
+      await uni.showToast({ title: '下单失败', icon: 'none' })
+      return
+    }
+    const payRes = await Apis.charge.wxJsApiPay({
+      data: {
+        orderNumber,
+      },
+    })
+    uni.hideLoading()
+    await useUserStore().getWxCommonPayment(payRes.data)
+    await useUserStore().paySuccess('charge-buy-a-ticket-list', '/subPack-charge/chargeBuyaTicketList/chargeBuyaTicketList')
+  }
+  catch (error) {
+    uni.hideLoading()
+    console.error('支付失败:', error)
+    await useUserStore().payError('charge-buy-a-ticket-list', '/subPack-charge/chargeBuyaTicketList/chargeBuyaTicketList')
+  }
+}
+
+/**
+ * 申请退款
+ */
+function refund() {
+  useGlobalMessage().confirm({
+    title: '提示',
+    msg: '退款按照购券记录进行逐笔退款,可能产生多笔退款到账记录,请注意查收。',
+    success: async () => {
+      const res = await Apis.charge.userCouponRefund({})
+      if (res.code === '00000') {
+        useGlobalMessage().confirm({
+          title: '提示',
+          msg: '申请退款成功,预计3个工作日内分一笔或多笔退还,到期如未到账请联系客服!',
+          success: () => {
+            router.back()
+          },
+          fail: () => {
+          },
+        })
+      }
+    },
+  })
+}
+</script>
+
+<template>
+  <view class="min-h-screen" :style="{ backgroundImage: `url(${StaticUrl}/buy-center-bg.png)`, backgroundSize: 'cover', backgroundPosition: 'center' }">
+    <wd-navbar
+      title="购券中心" :custom-style="`background-color: rgba(226, 255, 145, ${opcity})`" :bordered="false"
+      :z-index="99" safe-area-inset-top left-arrow fixed @click-left="router.back()"
+    />
+    <view :style="{ paddingTop: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 12}px` }" class="px24rpx">
+      <view class="h-342rpx" />
+      <view class="relative rounded-16rpx from-[#E1FF90] via-[#FAFFEE] to-[#FFFFFF] bg-gradient-to-b p-12rpx">
+        <image
+          class="absolute right-12rpx top-12rpx h-144rpx w-144rpx"
+          :src="`${StaticUrl}/buy-center-cardIcon.png`"
+          mode="scaleToFill"
+        />
+        <view class="mt-16rpx text-center">
+          <view class="text-28rpx">
+            平台券金额
+          </view>
+          <view class="mt-16rpx text-28rpx font-800">
+            {{ couponBalance }}
+            <text class="text-24rpx">
+              元
+            </text>
+          </view>
+        </view>
+        <view class="mb-16rpx mt-28rpx flex items-center justify-around text-28rpx">
+          <view @click="router.push({ name: 'charge-buy-a-ticket-list' })">
+            充值记录
+          </view>
+          <view class="h-40rpx w-2rpx bg-#E6E6E6" />
+          <view @click="refund">
+            退款
+          </view>
+        </view>
+      </view>
+      <view class="mt-20rpx">
+        <view class="text-32rpx font-bold">
+          本次充值
+        </view>
+        <view class="mt-24rpx flex flex-wrap items-center justify-start gap-20rpx">
+          <view v-for="(item, index) in rechargeLevels" :key="index" class="h-136rpx w-220rpx text-center" :class="selectIndex === index ? 'text-[#9ED605]' : 'text-#2B303Ac'" :style="{ backgroundImage: `url(${StaticUrl}/${selectIndex === index ? 'buy-center-rechargesel' : 'buy-center-recharge'}.png)`, backgroundSize: 'cover', backgroundPosition: 'center' }" @click="selectVoucher(index)">
+            <view class="h-80rpx text-28rpx font-bold line-height-[80rpx]">
+              {{ item?.money }}<text class="text-24rpx">
+                元
+              </text>
+            </view>
+            <view class="h-46rpx text-28rpx font-bold line-height-[46rpx] font-italic">
+              {{ item?.name }}
+            </view>
+          </view>
+        </view>
+      </view>
+      <view class="mt-24rpx">
+        <view class="font-bold">
+          温馨提示
+        </view>
+        <view class="mt-24rpx text-24rpx text-#AAAAAA">
+          <view>1.平台券仅限本平台使用,不可转赠和出售。</view>
+          <view class="mt-20rpx">
+            2.平台券仅可用于本平台充电服务,若订单金额超过平台券余额,
+            需补足差价;若余额有剩余,可留存下次使用;剩余平台券余额支
+            持手动退款。
+          </view>
+          <view class="mt-20rpx">
+            3.平台保留调整平台券使用规则的权利,调整前将提前公告。
+            如有疑问,请联系客服。
+          </view>
+        </view>
+      </view>
+      <view
+        class="fixed bottom-66rpx left-24rpx h-100rpx w-702rpx rounded-16rpx text-center text-28rpx font-800 line-height-[100rpx]"
+        :class="selectIndex === undefined ? 'bg-[#CCCCCC] text-white' : 'bg-[linear-gradient(90deg,#DBFC81_0%,#9ED605_100%)] text-black shadow-[inset_0rpx_6rpx_20rpx_2rpx_#FFFFFF]'"
+        @click="submitPay"
+      >
+        立即支付
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped></style>

+ 392 - 0
src/subPack-charge/components/plate/index.vue

@@ -0,0 +1,392 @@
+<script setup lang="ts">
+import { computed, ref, watch } from 'vue'
+
+// Props 定义
+const props = defineProps({
+  plateNumber: {
+    type: Array as () => (string | number)[],
+    default: () => Array.from({ length: 8 }, () => ''),
+  },
+})
+
+// Emits 定义
+const emit = defineEmits<{
+  myPlateChange: [plateNumber: (string | number)[]]
+}>()
+
+// 响应式数据
+const show = ref(false)
+const index = ref(-1)
+const areaDatas: string[] = [
+  '京',
+  '津',
+  '渝',
+  '沪',
+  '冀',
+  '晋',
+  '辽',
+  '吉',
+  '黑',
+  '苏',
+  '浙',
+  '皖',
+  '闽',
+  '赣',
+  '鲁',
+  '豫',
+  '鄂',
+  '湘',
+  '粤',
+  '琼',
+  '川',
+  '贵',
+  '云',
+  '陕',
+  '甘',
+  '青',
+  '蒙',
+  '桂',
+  '宁',
+  '新',
+  '藏',
+  '使',
+  '领',
+  '',
+  '',
+  '',
+  '',
+  '',
+  '',
+]
+const characterDatas: (number | string)[] = [
+  0,
+  1,
+  2,
+  3,
+  4,
+  5,
+  6,
+  7,
+  8,
+  9,
+  'A',
+  'B',
+  'C',
+  'D',
+  'E',
+  'F',
+  'G',
+  'H',
+  'J',
+  'K',
+  'L',
+  'M',
+  'N',
+  'P',
+  'Q',
+  'R',
+  'S',
+  'T',
+  'U',
+  'V',
+  'W',
+  'X',
+  'Y',
+  'Z',
+  '挂',
+  '警',
+  '学',
+  '港',
+  '澳',
+]
+
+// 内部车牌数组(用于本地操作)
+const localPlateNumber = ref<(string | number)[]>([...props.plateNumber])
+
+// 计算属性
+const currentDatas = computed(() => {
+  return index.value === 0 ? areaDatas : characterDatas
+})
+
+// 方法
+function handleChange(idx: number) {
+  index.value = idx
+  show.value = true
+}
+
+function handleClickKeyBoard(item: string | number, idx: number) {
+  // 索引为1时不能选择数字(0-9)
+  if (index.value === 1 && idx < 10)
+    return
+  // 索引2-5时不能选择特殊字符(索引>33)
+  if (index.value > 1 && index.value < 6 && idx > 33)
+    return
+
+  if (index.value < 8) {
+    localPlateNumber.value[index.value] = item
+    emit('myPlateChange', [...localPlateNumber.value])
+  }
+  if (index.value < 7) {
+    index.value++
+  }
+}
+
+// 重置
+function handleReset() {
+  index.value = 0
+  for (let i = 0; i < 8; i++) {
+    localPlateNumber.value[i] = ''
+  }
+  emit('myPlateChange', [...localPlateNumber.value])
+}
+
+// 删除
+function handleDelete() {
+  localPlateNumber.value[index.value] = ''
+  emit('myPlateChange', [...localPlateNumber.value])
+  if (index.value > 0) {
+    index.value--
+  }
+}
+
+// 获取完整车牌号字符串
+function getPlateString() {
+  let plate = ''
+  localPlateNumber.value.forEach((item, idx) => {
+    if (idx === 1) {
+      plate = `${plate}${item}·`
+    }
+    else {
+      plate += item
+    }
+  })
+  return plate
+}
+
+// 车牌号格式校验
+function validatePlateNumber() {
+  const plateNumber = getPlateString().replace(/·/g, '')
+
+  if (!plateNumber) {
+    return { valid: false, message: '请输入车牌号' }
+  }
+
+  // 长度校验:普通车牌7位,新能源8位
+  if (plateNumber.length < 7) {
+    return { valid: false, message: '车牌号长度不足,请输入完整车牌号' }
+  }
+  if (plateNumber.length > 8) {
+    return { valid: false, message: '车牌号长度超出限制' }
+  }
+
+  // 第一位必须是省份简称
+  const firstChar = plateNumber.charAt(0)
+  if (!areaDatas.includes(firstChar)) {
+    return { valid: false, message: '车牌号首位必须是省份简称' }
+  }
+
+  // 第二位必须是大写字母
+  const secondChar = plateNumber.charAt(1)
+  if (!/^[A-Z]$/.test(secondChar)) {
+    return { valid: false, message: '车牌号第二位必须是字母' }
+  }
+
+  // 普通车牌正则:省份+字母+5位字母数字(含特殊车牌字符)
+  const normalPlateReg = /^[\u4E00-\u9FA5][A-Z][A-Z0-9]{4}[A-Z0-9挂港澳使领警]$/
+  // 新能源车牌正则:省份+字母+6位字母数字
+  const newEnergyPlateReg = /^[\u4E00-\u9FA5][A-Z][A-Z0-9]{6}$/
+
+  if (!normalPlateReg.test(plateNumber) && !newEnergyPlateReg.test(plateNumber)) {
+    return { valid: false, message: '车牌号格式不正确' }
+  }
+
+  return { valid: true, message: '' }
+}
+
+// 暴露方法给父组件
+defineExpose({
+  validatePlateNumber,
+  getPlateString,
+  reset: handleReset,
+})
+
+// 监听 props 变化同步到本地
+watch(() => props.plateNumber, (newVal) => {
+  localPlateNumber.value = [...newVal]
+}, { deep: true })
+</script>
+
+<template>
+  <view>
+    <view class="plate" :class="{ show }">
+      <view class="item" :class="{ active: index === 0 }" @click="handleChange(0)">
+        {{ localPlateNumber[0] }}
+        <text class="triangle" />
+      </view>
+      <view class="item" :class="{ active: index === 1 }" @click="handleChange(1)">
+        {{ localPlateNumber[1] }}
+      </view>
+      <view class="point">
+        ●
+      </view>
+      <view class="item" :class="{ active: index === 2 }" @click="handleChange(2)">
+        {{ localPlateNumber[2] }}
+      </view>
+      <view class="item" :class="{ active: index === 3 }" @click="handleChange(3)">
+        {{ localPlateNumber[3] }}
+      </view>
+      <view class="item" :class="{ active: index === 4 }" @click="handleChange(4)">
+        {{ localPlateNumber[4] }}
+      </view>
+      <view class="item" :class="{ active: index === 5 }" @click="handleChange(5)">
+        {{ localPlateNumber[5] }}
+      </view>
+      <view class="item" :class="{ active: index === 6 }" @click="handleChange(6)">
+        {{ localPlateNumber[6] }}
+      </view>
+      <view class="item new-energy" :class="{ active: index === 7 }" @click="handleChange(7)">
+        <view v-if="localPlateNumber[7] || localPlateNumber[7] === 0">
+          <text>{{ localPlateNumber[7] }}</text>
+        </view>
+        <wd-icon v-else name="add" size="13px" color="#9ED605" />
+      </view>
+    </view>
+    <section class="panel" :class="{ show }">
+      <view class="header">
+        <view @click="handleReset">
+          重置
+        </view>
+        <view @click="show = false">
+          完成
+        </view>
+      </view>
+      <view class="panelList">
+        <view v-for="(item, idx) of currentDatas" :key="idx" class="item">
+          <view
+            v-if="item !== ''"
+            :class="{ disabled: (index === 1 && idx < 10) || (index > 1 && index < 6 && idx > 33) }"
+            @click="handleClickKeyBoard(item, idx)"
+          >
+            {{ item }}
+          </view>
+        </view>
+        <view class="item backspace" :class="{ special: index === 0 }" @click="handleDelete">
+          ×
+        </view>
+      </view>
+    </section>
+  </view>
+</template>
+
+<style scoped lang='scss'>
+    .plate {
+        display: flex;
+        justify-content: space-between;
+        .item {
+            width: 64rpx;
+            height: 80rpx;
+            background-color: #F3F4F7;
+            border-radius: 8rpx;
+            text-align: center;
+            line-height: 80rpx;
+            font-size: 32rpx;
+            color: rgba(0,0,0,0.90);
+            font-weight: bold;
+            position: relative;
+            &.active {
+                background-color: #bbbbbb;
+            }
+        }
+        .new-energy {
+            box-sizing: border-box;
+            border: 2rpx dashed #9ED605;
+            font-weight: bold;
+            uni-icons {
+                display: flex;
+                align-items: center;
+                justify-content: center;
+            }
+        }
+        .point {
+            height: 80rpx;
+            text-align: center;
+            line-height: 80rpx;
+            color: #BDC4CC;
+            font-size: 18rpx;
+        }
+        .triangle {
+            width: 0;
+            height: 0;
+            border: 6rpx solid transparent;
+            border-right-color: #00C69D;
+            border-bottom-color: #00C69D;
+            border-radius: 1rpx 2rpx 1rpx;
+            position: absolute;
+            right: 6rpx;
+            bottom: 6rpx;
+        }
+    }
+    .panel {
+        position: fixed;
+        left: 0;
+        width: 100%;
+        bottom: 0;
+        z-index: 999;
+        box-sizing: border-box;
+        background-color: #F5F5F5;
+        transition: all 0.3s ease;
+        transform: translateY(100%);
+        &.show {
+            transform: translateX(0);
+        }
+        .header {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            padding: 0 24rpx;
+            height: 96rpx;
+            color: #9ED605;
+            font-size: 34rpx;
+        }
+        .panelList {
+            padding: 0 19rpx 20rpx;
+            .item {
+                display: inline-block;
+                width: calc((100% - 72rpx) / 10);
+                height: 84rpx;
+                margin-right: 8rpx;
+                margin-bottom: 8rpx;
+                vertical-align: top;
+                view {
+                    width: 100%;
+                    height: 84rpx;
+                    line-height: 84rpx;
+                    border-radius: 6rpx;
+                    background: #FEFFFE;
+                    font-size: 32rpx;
+                    color: rgba(0,0,0,0.90);
+                    font-weight: bold;
+                    text-align: center;
+                    &.disabled {
+                        background-color: rgba(254, 255, 254, 0.6);
+                        color: rgba(0, 0, 0, 0.23);
+                    }
+                }
+                &:nth-of-type(10n) {
+                    margin-right: 0;
+                }
+            }
+            .backspace {
+                vertical-align: top;
+                font-size: 48rpx;
+                font-weight: bold;
+                text-align: center;
+                height: 84rpx;
+                line-height: 84rpx;
+                border-radius: 6rpx;
+                background: #FEFFFE;
+                color: rgba(0,0,0,0.90);
+            }
+        }
+    }
+</style>

+ 158 - 33
src/subPack-charge/index/index.vue

@@ -49,6 +49,8 @@ onMounted(() => {
 onShow(() => {
   refresh()
   getUserAccountInfo()
+  getDefaultVehicle()
+  queryVehicleList()
 })
 onPageScroll((e) => {
   const calculatedOpacity = e.scrollTop / 100
@@ -68,11 +70,60 @@ async function getUserAccountInfo() {
   const res = await Apis.charge.getMemberInfo({})
   userAccountInfo.value = res.data
 }
+
+/**
+ * 获取用户默认车辆
+ */
+const defaultVehicle = ref<Api.UserVehicleVO>()
+async function getDefaultVehicle() {
+  const res = await Apis.charge.default({})
+  defaultVehicle.value = res.data
+}
+
+// 获取车辆列表判断是否提示用户添加车辆
+async function queryVehicleList() {
+  const res = await Apis.charge.vehicleList({})
+  if (res.data.length === 0) {
+    useGlobalMessage().confirm({
+      title: '充电车主专享',
+      msg: '绑定车牌可享充电停车费减免及更多权益',
+      success: () => {
+        router.push({ name: 'charge-plate-list' })
+      },
+    })
+  }
+}
+
 // 处理筛选项点击的方法
 function handleFilterClick(filterKey: number) {
   activeFilter.value = filterKey
   reload()
 }
+
+/**
+ * 申请退款
+ */
+function refund() {
+  useGlobalMessage().confirm({
+    title: '提示',
+    msg: '退款按照购券记录进行逐笔退款,可能产生多笔退款到账记录,请注意查收。',
+    success: async () => {
+      const res = await Apis.charge.userCouponRefund({})
+      if (res.code === '00000') {
+        useGlobalMessage().confirm({
+          title: '提示',
+          msg: '申请退款成功,预计3个工作日内分一笔或多笔退还,到期如未到账请联系客服!',
+          success: () => {
+            refresh()
+          },
+          fail: () => {
+            refresh()
+          },
+        })
+      }
+    },
+  })
+}
 </script>
 
 <template>
@@ -98,55 +149,129 @@ function handleFilterClick(filterKey: number) {
             class="h-100rpx w-100rpx"
             :src="`${userInfo && userInfo?.avatarUrl ? userInfo.avatarUrl : `${StaticUrl}/charge-default-avatar.png`}`"
           />
-          <view class="flex items-center gap-16rpx">
-            <view class="text-32rpx text-#2B303A font-bold">
-              {{ userInfo?.nickName || '暂未登录' }}
-            </view>
-            <view v-if="!token" class="text-24rpx text-#9ED605" @click="router.replace({ name: 'smqjh-login' })">
-              授权登录
+          <view>
+            <view class="flex items-center gap-16rpx">
+              <view class="text-32rpx text-#2B303A font-bold">
+                {{ userInfo?.nickName || '暂未登录' }}
+              </view>
+              <view v-if="!token" class="text-24rpx text-#9ED605" @click="router.replace({ name: 'smqjh-login' })">
+                授权登录
+              </view>
+              <view v-else class="rounded-8rpx bg-#E9FFAC px12rpx py4rpx text-24rpx text-#9ED605">
+                {{ userInfo.channelName }}
+              </view>
             </view>
-            <view v-else class="rounded-8rpx bg-#9ED605 px12rpx py4rpx text-24rpx text-#FFF opacity-70">
-              {{ userInfo.channelName }}
+            <view class="mt-16rpx text-28rpx font-bold">
+              {{ userInfo?.mobile }}
             </view>
           </view>
         </view>
-        <view class="mt-20rpx flex items-center justify-between gap-18rpx">
-          <view class="bg-#F3FFD1 p-24rpx">
-            <view class="flex items-center justify-between gap-48rpx text-28rpx text-#2B303A font-bold">
-              <view>我的积分</view>
-              <view>可用充电余额</view>
+        <view class="mt-20rpx rounded-16rpx bg-#F3FFD1 px-24rpx py-30rpx">
+          <view class="relative flex items-center justify-between">
+            <view class="flex items-center gap-16rpx">
+              <view class="h-40rpx w-40rpx">
+                <image
+                  class="h-40rpx w-40rpx"
+                  :src="`${StaticUrl}/charge-mine-wallet.png`"
+                  mode="scaleToFill"
+                />
+              </view>
+              <view class="text-32rpx font-bold">
+                我的钱包
+              </view>
+            </view>
+            <view class="absolute right-[-24rpx] h-44rpx w-120rpx rounded-[22rpx_0_0_22rpx] bg-#9ED605 text-center text-28rpx text-#FFF line-height-[44rpx]" @click="refund">
+              退还
+            </view>
+          </view>
+          <view class="mt-24rpx flex items-center justify-around">
+            <view class="text-center">
+              <view class="text-28rpx">
+                企业积分
+              </view>
+              <view class="mt-16rpx text-40rpx text-#9ED605 font-bold">
+                {{ userAccountInfo?.availablePoints || '0.00' }}
+              </view>
             </view>
-            <view class="mt-24rpx flex items-center gap-30rpx text-28rpx text-#9ED605 font-500">
-              <view class="flex items-center gap-20rpx">
-                <text class="w-120rpx overflow-hidden truncate whitespace-nowrap">
-                  {{ userAccountInfo?.availablePoints || '0.00' }}
-                </text>
-                <image class="h-30rpx w-30rpx" :src="`${StaticUrl}/charge-acc.png`" />
-              </view>
-              <view class="w-120rpx overflow-hidden truncate whitespace-nowrap">
-                <text class="text-20rpx">
+            <view class="h-92rpx w-2rpx bg-#9ED605" />
+            <view class="text-center">
+              <view class="text-28rpx">
+                平台券
+              </view>
+              <view class="mt-16rpx text-40rpx text-#9ED605 font-bold">
+                <text class="text-24rpx">
-                </text>
-                {{ userAccountInfo?.balance || '0.00' }}
+                </text>{{ userAccountInfo?.couponBalance || '0.00' }}
               </view>
             </view>
           </view>
-          <view class="flex items-center gap-20rpx bg-#F3FFD1 p-24rpx" @click="router.push({ name: 'charge-order-list' })">
-            <view>
-              <view class="text-28rpx font-bold">
+        </view>
+      </view>
+      <view class="mt-24rpx flex justify-between">
+        <view class="flex items-center gap-18rpx rounded-16rpx bg-#FFF px-24rpx py-28rpx">
+          <view>
+            <view class="flex items-center gap-16rpx" @click="router.push({ name: 'charge-order-list' })">
+              <text class="text-32rpx font-bold">
                 充电订单
+              </text>
+              <view class="h-30rpx w-30rpx rounded-50% bg-#000 text-center line-height-[30rpx]">
+                <wd-icon name="arrow-right" color="#FFFFFF" size="30rpx" />
               </view>
-              <view class="mt-24rpx text-28rpx text-#9ED605 font-500">
-                {{ userAccountInfo?.chargingOrderCount || '0' }}
+            </view>
+            <view class="mt-16rpx text-24rpx">
+              查看充电订单
+            </view>
+          </view>
+          <view class="h-100rpx w-100rpx">
+            <image
+              class="h-100rpx w-100rpx"
+              :src="`${StaticUrl}/charge-mine-order.png`"
+              mode="scaleToFill"
+            />
+          </view>
+        </view>
+        <view class="flex items-center gap-18rpx rounded-16rpx bg-#FFF px-24rpx py-28rpx" @click="router.push({ name: 'charge-voucher', params: { couponBalance: userAccountInfo?.couponBalance } })">
+          <view>
+            <view class="flex items-center gap-16rpx">
+              <text class="text-32rpx font-bold">
+                购券中心
+              </text>
+              <view class="h-30rpx w-30rpx rounded-50% bg-#000 text-center line-height-[30rpx]">
+                <wd-icon name="arrow-right" color="#FFFFFF" size="30rpx" />
               </view>
             </view>
-            <image class="h-80rpx w-80rpx" :src="`${StaticUrl}/charge-order.png`" />
+            <view class="mt-16rpx text-24rpx">
+              平台券充值
+            </view>
+          </view>
+          <view class="h-100rpx w-100rpx">
+            <image
+              class="h-100rpx w-100rpx"
+              :src="`${StaticUrl}/charge-mine-voucher.png`"
+              mode="scaleToFill"
+            />
           </view>
         </view>
       </view>
-      <!-- <view class="mt-24rpx">
-        <wd-swiper :list="swiperList" :height="100" :indicator="false" value-key="advertImg" />
-      </view> -->
+      <view class="mt-24rpx flex items-center justify-between rounded-16rpx bg-#FFF p-20rpx">
+        <view class="text-32rpx font-bold">
+          我的车辆
+        </view>
+        <!-- 无车辆时显示添加提示 -->
+        <view v-if="!defaultVehicle" class="flex items-center gap-10rpx" @click="router.push({ name: 'charge-plate-list' })">
+          <view class="text-26rpx">
+            添加车辆,享更多权益
+          </view>
+          <view class="h-50rpx w-50rpx rounded-50% bg-[linear-gradient(90deg,#DBFC81_0%,#9ED605_100%)] text-center text-30rpx text-#FFF font-bold line-height-[50rpx]">
+            +
+          </view>
+        </view>
+        <!-- 有车辆时显示车牌和管理入口 -->
+        <view v-else class="flex items-center gap-50rpx text-26rpx text-#9ED605" @click="router.push({ name: 'charge-plate-list' })">
+          <view>{{ defaultVehicle.licensePlate }}</view>
+          <view>管理>></view>
+        </view>
+      </view>
       <view class="mt-24rpx flex items-center gap-20rpx">
         <view
           v-for="option in filterOptions"

+ 2 - 2
src/subPack-common/afterSales/index.vue

@@ -167,7 +167,7 @@ function handleChangeMoney() {
               </view>
             </view>
           </wd-radio>
-          <view class="mt24rpx">
+          <!-- <view class="mt24rpx">
             <wd-radio :value="2">
               <view>
                 <view class="text-left text-28rpx font-semibold">
@@ -178,7 +178,7 @@ function handleChangeMoney() {
                 </view>
               </view>
             </wd-radio>
-          </view>
+          </view> -->
         </wd-radio-group>
       </view>
       <view class="mt20rpx rounded-16rpx bg-white px24rpx py28rpx">

+ 100 - 10
src/subPack-common/afterSalesDetail/index.vue

@@ -1,5 +1,6 @@
 <script setup lang="ts">
 import { handleCommonRefundOrderCancel, refundStatus } from '../afterSalesList/index'
+import router from '@/router'
 import { StaticUrl } from '@/config'
 
 definePage({
@@ -33,6 +34,12 @@ function copyToClipboard(text: string) {
     showToast: true,
   })
 }
+function handleGOCoupon() {
+  if (!refundOrderInfo.value?.couponBaseInfoDTO) {
+    return useGlobalToast().show('优惠券不存在')
+  }
+  router.push({ name: 'xsb-coupon', params: { couponId: refundOrderInfo.value?.couponBaseInfoDTO?.allowanceId, activeTab: refundOrderInfo.value?.isAll ? '0' : '1' } })
+}
 </script>
 
 <template>
@@ -122,21 +129,78 @@ function copyToClipboard(text: string) {
       </view>
       <view class="mt24rpx flex items-center justify-between text-28rpx">
         <view class="text-#222 font-semibold">
-          退款金额
+          {{ refundStatus.ReturnCompleted == refundOrderInfo.returnMoneySts ? '实' : '应' }}退款金额
         </view>
         <view class="text-#FF4D3A font-semibold">
           ¥{{ refundOrderInfo.refundAmount }}
         </view>
       </view>
-      <view class="mt20rpx text-24rpx text-#AAAAAA">
-        申请通过后,将退回至原账户
+      <view v-if="refundStatus.ReturnCompleted == refundOrderInfo.returnMoneySts" class="mt20rpx flex flex-wrap items-center gap-20rpx text-28rpx">
+        <view class="flex items-center">
+          <view class="text-#222 font-semibold">
+            退款金额
+          </view>
+          <view class="text-#FF4D3A font-semibold">
+            ¥{{ refundOrderInfo.userRefundMoney }}
+          </view>
+        </view>
+        <view class="ml20rpx flex items-center">
+          <view class="text-#222 font-semibold">
+            退还积分:
+          </view>
+          <view class="text-#FF4D3A font-semibold">
+            {{ refundOrderInfo.refundScore }}
+          </view>
+        </view>
+        <view class="flex items-center">
+          <view class="text-#222 font-semibold">
+            退还优惠券:
+          </view>
+          <view class="text-#FF4D3A font-semibold">
+            ¥{{ refundOrderInfo.isAll ? refundOrderInfo.couponBaseInfoDTO?.discountMoney : '0' }}
+          </view>
+        </view>
       </view>
-      <view v-if="refundOrderInfo.returnMoneySts == refundStatus.ReturnCompleted" class="mt20rpx flex items-center text-24rpx text-#AAAAAA">
+      <!-- <view class="mt20rpx text-24rpx text-#AAAAAA">
+        申请通过后,将退回至原账户
+      </view> -->
+      <!-- <view v-if="refundOrderInfo.returnMoneySts == refundStatus.ReturnCompleted" class="mt20rpx flex items-center text-24rpx text-#AAAAAA">
         <view>退还金额:{{ refundOrderInfo.userRefundMoney }}</view>
         <view class="ml20rpx">
           退还积分:{{ refundOrderInfo.refundScore }}(已过期{{ refundOrderInfo.refundExpiredScore }})
         </view>
+      </view> -->
+    </view>
+    <view class="mt20rpx rounded-16rpx bg-white p24rpx">
+      <view class="text-28rpx font-semibold">
+        优惠券处理
       </view>
+      <view v-if="refundOrderInfo.couponBaseInfoDTO" class="mt10rpx text-24rpx">
+        使用的优惠券:{{ refundOrderInfo.couponBaseInfoDTO.activityName }}
+      </view>
+      <view class="mb24rpx mt10rpx text-24rpx">
+        <view> 处理说明:</view>
+
+        <template v-if="refundStatus.PendingAudit == refundOrderInfo.returnMoneySts">
+          <view> 当前售后单为全额退款。审核通过后,优惠券退还,有效期不变;</view>
+
+          <view>审核不通过,则优惠券保持原状。</view>
+        </template>
+        <template v-else-if="[refundStatus.Rejected, refundStatus.Cancel].includes(refundOrderInfo.returnMoneySts as number)">
+          <view> 优惠券保持已使用状态,不退还。</view>
+        </template>
+        <template v-else-if="refundStatus.ReturnCompleted == refundOrderInfo.returnMoneySts">
+          <view v-if="refundOrderInfo.isAll">
+            <view> 有效期不变至: {{ refundOrderInfo.couponBaseInfoDTO?.expirationTime }}</view>
+          </view>
+          <view v-else>
+            <view> 优惠券已使用,不退回</view>
+          </view>
+        </template>
+      </view>
+      <wd-button v-if="refundStatus.ReturnCompleted == refundOrderInfo.returnMoneySts" @click="handleGOCoupon">
+        查看优惠券
+      </wd-button>
     </view>
     <view class="mt20rpx rounded-16rpx bg-white p24rpx">
       <view class="grid grid-cols-5 py24rpx text-28rpx text-#222">
@@ -252,21 +316,47 @@ function copyToClipboard(text: string) {
           ¥{{ refundOrderInfo.freightAmount }}
         </view>
       </view>
+      <view v-if="refundOrderInfo.couponBaseInfoDTO" class="mt24rpx flex items-center justify-between">
+        <view class="text-28rpx">
+          优惠券
+        </view>
+        <view class="text-#FF4A39 font-semibold">
+          -¥{{ refundOrderInfo.couponBaseInfoDTO?.discountMoney }}
+        </view>
+      </view>
       <view class="mt24rpx flex items-center justify-between">
         <view class="text-28rpx">
           积分({{ refundOrderInfo.omsOrderVo.offsetPoints }})
         </view>
         <view class="text-#FF4A39 font-semibold">
-          ¥{{ refundOrderInfo.omsOrderVo.offsetPointsMoney }}
+          -¥{{ refundOrderInfo.omsOrderVo.offsetPointsMoney }}
         </view>
       </view>
       <view class="my24rpx h2rpx w-full bg-#F0F0F0" />
-      <view class="flex items-center justify-between">
-        <view class="text-28rpx font-semibold">
-          合计:
+      <view class="flex items-center">
+        <view class="flex items-center justify-end">
+          <view class="text-28rpx font-semibold">
+            总计:
+          </view>
+          <view class="ml8rpx text-28rpx text-#FF4D3A font-semibold">
+            ¥{{ refundOrderInfo.omsOrderVo.orderMoney }}
+          </view>
+        </view>
+        <view class="ml10rpx flex items-center">
+          <view clas="text-28rpx">
+            共减
+          </view>
+          <view class="ml5rpx text-28rpx text-#FF4D3A font-semibold">
+            ¥{{ Number(refundOrderInfo.omsOrderVo.offsetPointsMoney) + Number(refundOrderInfo.couponBaseInfoDTO?.discountMoney) }}
+          </view>
         </view>
-        <view class="ml8rpx text-28rpx text-#FF4D3A font-semibold">
-          ¥{{ refundOrderInfo.omsOrderVo.orderMoney }}
+        <view class="ml10rpx flex items-center">
+          <view clas="text-28rpx">
+            实付款
+          </view>
+          <view class="ml5rpx text-28rpx text-#FF4D3A font-semibold">
+            ¥{{ refundOrderInfo.omsOrderVo?.actualTotal }}
+          </view>
         </view>
       </view>
     </view>

+ 2 - 6
src/subPack-common/integral/index.vue

@@ -14,7 +14,6 @@ const { data: info } = useRequest(() =>
   Apis.xsb.findUserPoints({}),
 )
 
-const type = ['充值', '下单', '退款', '过期积分', '退款过期积分']
 const { data: pointList, isLastPage, page } = usePagination((pageNum, pageSize) => Apis.xsb.findUserPointsPage({ data: { pageNum, pageSize } }), { data: resp => resp.data?.list, initialPage: 1, initialPageSize: 10, immediate: true, append: true })
 
 function handleScrollBottom() {
@@ -29,9 +28,6 @@ async function getNavList() {
   navTabTypeList.value = res.data?.list || []
 }
 getNavList()
-function getLabel(val: string) {
-  return navTabTypeList.value.find(i => i.value === val)?.name || '未知类型'
-}
 </script>
 
 <template>
@@ -77,10 +73,10 @@ function getLabel(val: string) {
       <view v-for="item, index in pointList" :key="item.pointsId" class="bg-white p-24rpx">
         <view class="flex items-center justify-between text-32rpx font-semibold">
           <view class="text-[#222]">
-            {{ (type[Number(item.pointsType) - 1] == '下单' ? getLabel(item.businessType) : type[Number(item.pointsType) - 1]) || '未知状态' }}
+            {{ item.businessTypeName || '未知状态' }}
           </view>
           <view class="text-[#FF4A39]">
-            {{ item?.variablePoints || 0 }}
+            {{ item?.pointsTypeName }}{{ item?.variablePoints || 0 }}
           </view>
         </view>
         <view class="mt-20rpx flex items-center justify-between text-28rpx text-[#AAAAAA]">

+ 2 - 1
src/subPack-film/index/index.vue

@@ -10,6 +10,7 @@ definePage({
     backgroundColorBottom: '#fff',
   },
 })
+const addressStore = useAddressStore()
 
 const hotList = ref<Api.filmMovieList>([
 
@@ -30,7 +31,7 @@ function handleBuy(item: Api.filmMovieList) {
 
 async function getList(showSt: number) {
   uni.showLoading({ title: '加载中' })
-  const res = await Apis.film.getMovieList({ data: { showSt, pageNum: 1, pageSize: 8 } })
+  const res = await Apis.film.getMovieList({ data: { showSt, pageNum: 1, pageSize: 8, cityName: addressStore.city } })
   console.log(res, '请求')
   if (!res.data) {
     useGlobalToast().show('暂无该商品查看权限!')

+ 2 - 2
src/subPack-film/movie/index.vue

@@ -24,7 +24,7 @@ const tabList = reactive([
 ])
 
 const { data: hotList, isLastPage, page, reload, error, refresh } = usePagination((pageNum, pageSize) =>
-  Apis.film.getMovieList({ data: { showSt: 1, pageNum, pageSize } }), {
+  Apis.film.getMovieList({ data: { showSt: 1, pageNum, pageSize, cityName: addressStore.city } }), {
   data: resp => resp.data?.records,
   initialData: [],
   initialPage: 1,
@@ -44,7 +44,7 @@ const { data: filmList, isLastPage: isLastPage1, page: page1, reload: reload1, e
 })
 
 const { data: comingSoonList, isLastPage: isLastPage2, page: page2, reload: reload2, error: error2, refresh: refresh2 } = usePagination((pageNum, pageSize) =>
-  Apis.film.getMovieList({ data: { showSt: 2, pageNum, pageSize } }), {
+  Apis.film.getMovieList({ data: { showSt: 2, pageNum, pageSize, cityName: addressStore.city } }), {
   data: resp => resp.data?.records,
   initialData: [],
   initialPage: 1,

+ 47 - 0
src/subPack-refueling/commonTab/index.vue

@@ -7,15 +7,61 @@ definePage({
   },
 })
 
+onMounted(() => {
+  console.log('触发加载')
+  getPayQrcode()
+})
+
 function refuelingPay() {
   console.log('触发跳转')
   // router.push({ name: 'refueling-webview' })
+  // wx.openOfficialAccountArticle({
+  //   url: 'https://smqjh.admin.zswlgz.com/test.html', // 此处填写公众号的原始 ID
+  //   success: (res) => {
+  //   },
+  //   fail: (res) => {
+  //     console.log(res, 'err')
+  //   },
+  // })
   uni.navigateToMiniProgram({
     appId: 'wx0d252f6ed9755862', // 滴滴加油小程序appId
     path: 'packageA/pages/open-energy-pay/index?orderId=&tradeId=&appId=wx43b5b906cc30ed0b&path=/pages/index/index&envVersion=小程序回跳环境', // 滴滴加油收银台页面地址,需要拼接orderId和tradeId
     envVersion: 'release', // 固定release
   })
 }
+
+const payQrCode = ref('')
+async function getPayQrcode() {
+  const res = await Apis.refueling.getPayCode({
+    data: {
+      content: 'https://static.am.xiaojukeji.com/cf-terminal/oil/thanos-fe-oil/pages/open-energy-h5-pay/index.html?orderId=3521815091021431239&tradeId=20260226b3dd729612c482ab35c26879ca59118a10560142',
+    },
+  })
+  payQrCode.value = res.data
+}
+function testClick() {
+  uni.previewImage({
+    // 需要预览的图片链接列表
+    urls: [payQrCode.value],
+    // 当前显示图片的链接
+    current: payQrCode.value,
+    // 图片指示器样式
+    indicator: 'default',
+    // 是否可循环预览
+    loop: false,
+    showmenu: true,
+    // 长按图片显示操作菜单
+    longPressActions: {
+      itemList: ['发送给朋友', '保存到相册', '识别图中二维码'],
+    },
+    success: (res) => {
+      console.log('previewImage res', res)
+    },
+    fail: (err) => {
+      console.log('previewImage err', err)
+    },
+  })
+}
 </script>
 
 <template>
@@ -23,6 +69,7 @@ function refuelingPay() {
     <wd-button @click="refuelingPay">
       加油充值
     </wd-button>
+    <image :src="payQrCode" @click="testClick" />
   </view>
 </template>
 

+ 277 - 0
src/subPack-refueling/orderDetaile/index.vue

@@ -0,0 +1,277 @@
+<script setup lang="ts">
+import router from '@/router'
+
+definePage({
+  name: 'refueling-orderDetail',
+  islogin: true,
+  style: {
+    navigationBarTitleText: '订单详情',
+    navigationStyle: 'custom',
+  },
+})
+
+const orderNo = ref('')
+const orderInfo = ref<Api.xsbOrderList>()
+const oilInfo = computed<Api.OmsOrderOilVO | undefined>(() => orderInfo.value?.omsOrderOilVO)
+// 状态映射
+const statusMap = new Map([
+  [1, '待支付'],
+  [2, '已支付'],
+  [6, '已退款'],
+  [9, '已取消'],
+])
+
+const { send } = useRequest(
+  (no: string) => Apis.xsb.orderInfo({ data: { orderNo: no } }),
+  { immediate: false },
+).onSuccess((res) => {
+  orderInfo.value = res.data.data
+})
+
+onLoad((options: any) => {
+  orderNo.value = options.orderNo || ''
+  if (orderNo.value) {
+    send(orderNo.value)
+  }
+})
+
+// 计算支付倒计时
+function getCountdownTime(): number {
+  if (!orderInfo.value?.expireTime)
+    return 0
+  const expireTime = new Date(orderInfo.value.expireTime).getTime()
+  const remaining = expireTime - Date.now()
+  return remaining > 0 ? remaining : 0
+}
+
+// 复制订单号
+function handleCopy(text?: string) {
+  if (!text)
+    return
+  uni.setClipboardData({
+    data: text,
+    success: () => {
+      useGlobalToast().show('复制成功')
+    },
+  })
+}
+
+// 取消订单(占位)
+// async function handleCancelOrder() {
+//   await useUserStore().handleCommonCancelOrder(orderInfo.value?.orderNumber as string)
+//   send(orderNo.value)
+// }
+
+// 立即支付(占位)
+// function handlePay() {
+//   useUserStore().handleCommonPath(oilInfo.value?.payUrl as string)
+// }
+
+// 跳转首页
+// function handleGoHome() {
+//   router.pushTab({ name: 'home' })
+//   useTabbar().setTabbarItemActive('home')
+// }
+// function handleContact() {
+//   window.location.href = `weixin://dl/business/?appid=wx43b5b906cc30ed0b&path=subPack-xsb/orderDetaile/index&query=${orderInfo.value?.orderNumber}&env_version=release`
+// }
+</script>
+
+<template>
+  <wd-navbar
+    title="订单详情" :bordered="false" :z-index="99" safe-area-inset-top left-arrow placeholder fixed
+    @click-left="router.back()"
+  />
+  <view v-if="orderInfo && oilInfo" class="bg-#f5f5f5 pb200rpx pt20rpx">
+    <!-- 订单状态头部 -->
+    <view class="mx24rpx rounded-16rpx bg-white px28rpx py32rpx">
+      <view class="mb16rpx text-32rpx font-semibold">
+        订单状态:{{ statusMap.get(orderInfo.oilOrderStatus as number) || '-' }}
+      </view>
+
+      <!-- 待支付 - 倒计时和按钮 -->
+      <template v-if="orderInfo.oilOrderStatus === 1">
+        <view class="mb20rpx flex items-center text-26rpx text-#FF4A39">
+          剩余支付时间:<wd-count-down :time="getCountdownTime()" format="mm分ss秒" />
+        </view>
+        <!-- <view class="flex items-center gap-24rpx">
+          <wd-button plain custom-class="flex-1 cancel-btn" @click="handleCancelOrder">
+            取消订单
+          </wd-button>
+          <wd-button custom-class="flex-1" @click="handlePay">
+            立即支付 ¥{{ oilInfo.realMoney }}
+          </wd-button>
+        </view> -->
+      </template>
+
+      <!-- 已退款 - 联系客服 -->
+      <template v-else-if="orderInfo.oilOrderStatus === 6 || orderInfo.oilOrderStatus === 2">
+        <view class="flex items-center gap-48rpx">
+          <view class="flex flex-col items-center">
+            <wd-icon name="chat" size="48rpx" color="#333" />
+            <Zcontact>
+              <view class="mt-40rpx text-28rpx">
+                联系客服
+              </view>
+            </Zcontact>
+          </view>
+        </view>
+      </template>
+
+      <!-- 已取消 - 取消原因 -->
+      <template v-else-if="orderInfo.oilOrderStatus === 9">
+        <view class="text-26rpx text-#999">
+          取消原因:{{ orderInfo.cancelReason || '未按时支付' }}
+        </view>
+      </template>
+    </view>
+
+    <!-- 重要提示(已支付) -->
+    <view v-if="orderInfo.oilOrderStatus === 2" class="mx24rpx mt20rpx rounded-16rpx bg-white px28rpx py32rpx">
+      <view class="mb16rpx flex items-center text-26rpx text-#ff4d3a">
+        <wd-icon name="info-circle" size="28rpx" color="#ff4d3a" class="mr8rpx" />
+        重要提示
+      </view>
+      <view class="text-26rpx text-#666">
+        如需加油站线下开票,请向油站工作人员提供以下订单号:
+      </view>
+      <view class="mt12rpx flex items-center text-26rpx">
+        我方订单号:{{ orderInfo.orderNumber }}
+        <wd-icon name="content-copy" size="28rpx" color="#999" class="ml12rpx" @click="handleCopy(orderInfo?.orderNumber)" />
+      </view>
+    </view>
+
+    <!-- 退款信息(已退款) -->
+    <view v-if="orderInfo.oilOrderStatus === 6" class="mx24rpx mt20rpx rounded-16rpx bg-white px28rpx py32rpx">
+      <view class="mb20rpx text-30rpx font-semibold">
+        退款信息
+      </view>
+      <view class="text-26rpx text-#666 leading-48rpx">
+        <view>退款金额:¥{{ oilInfo.realMoney }}</view>
+        <view class="text-24rpx text-#999">
+          退还金额:¥{{ oilInfo.realMoney }} &nbsp; 退还积分:需联系客服处理
+        </view>
+        <view>退款时间:{{ oilInfo.refundTime || '-' }}</view>
+        <view>退款方式:原路返回</view>
+        <view>退款状态:退款成功</view>
+        <view>预计到账:1-3个工作日</view>
+      </view>
+    </view>
+
+    <!-- 订单编号 -->
+    <view class="mx24rpx mt20rpx rounded-16rpx bg-white px28rpx py32rpx">
+      <view class="mb20rpx text-30rpx font-semibold">
+        订单编号
+      </view>
+      <view class="text-26rpx text-#666 leading-48rpx">
+        <view>我方订单号:{{ orderInfo.orderNumber || '-' }}</view>
+        <view>小桔订单号:{{ oilInfo.xjOrderId || '-' }}</view>
+      </view>
+    </view>
+
+    <!-- 用户信息(已支付/已退款) -->
+    <view v-if="orderInfo.oilOrderStatus === 2 || orderInfo.oilOrderStatus === 6" class="mx24rpx mt20rpx rounded-16rpx bg-white px28rpx py32rpx">
+      <view class="mb20rpx text-30rpx font-semibold">
+        用户信息
+      </view>
+      <view class="text-26rpx text-#666 leading-48rpx">
+        <view>用户手机:{{ orderInfo.memberMobile || '-' }}</view>
+        <view>外部用户ID:{{ orderInfo.memberId || '-' }}</view>
+        <view>下单时间:{{ orderInfo.createTime || '-' }}</view>
+        <view>支付时间:{{ oilInfo.payTime || '-' }}</view>
+      </view>
+    </view>
+
+    <!-- 油站信息 -->
+    <view class="mx24rpx mt20rpx rounded-16rpx bg-white px28rpx py32rpx">
+      <view class="mb20rpx text-30rpx font-semibold">
+        油站信息
+      </view>
+      <view class="text-26rpx text-#666 leading-48rpx">
+        <view>油站名称:{{ oilInfo.brandName }}·{{ oilInfo.storeName }}</view>
+        <view>油站地址:{{ oilInfo.address || '-' }}</view>
+        <view>所在城市:{{ oilInfo.cityName || '-' }}</view>
+      </view>
+    </view>
+
+    <!-- 加油明细 -->
+    <view class="mx24rpx mt20rpx rounded-16rpx bg-white px28rpx py32rpx">
+      <view class="mb20rpx text-30rpx font-semibold">
+        加油明细
+      </view>
+      <view class="text-26rpx text-#666 leading-48rpx">
+        <view>油品名称:{{ oilInfo.itemName || '-' }}</view>
+        <view>枪号:{{ oilInfo.gunNo ? `${oilInfo.gunNo}号枪` : '-' }}</view>
+        <view>加油量:{{ oilInfo.quantity || '-' }}L</view>
+      </view>
+    </view>
+
+    <!-- 价格明细 -->
+    <view class="mx24rpx mt20rpx rounded-16rpx bg-white px28rpx py32rpx">
+      <view class="mb20rpx text-30rpx font-semibold">
+        价格明细
+      </view>
+      <view class="text-26rpx text-#666 leading-48rpx">
+        <view>门店价:¥{{ oilInfo.storePrice || '-' }}/L</view>
+        <view>平台价:¥{{ oilInfo.vipPrice || '-' }}/L</view>
+        <view>订单金额:¥{{ oilInfo.totalMoney }}</view>
+        <view>抵扣金额:-¥{{ oilInfo.promotionAmount }}</view>
+        <view>服务费:+¥{{ oilInfo.serviceFee }}</view>
+        <view v-if="orderInfo.oilOrderStatus === 6">
+          实付金额:¥{{ oilInfo.realMoney }}
+        </view>
+        <view v-else-if="orderInfo.oilOrderStatus === 9">
+          应付金额:¥{{ oilInfo.realMoney }}
+        </view>
+        <view v-else>
+          需付金额:¥{{ oilInfo.realMoney }}
+        </view>
+      </view>
+    </view>
+
+    <!-- 抵扣信息(已支付/已退款) -->
+    <view v-if="(orderInfo.oilOrderStatus === 2 || orderInfo.oilOrderStatus === 6) && oilInfo.allowanceId" class="mx24rpx mt20rpx rounded-16rpx bg-white px28rpx py32rpx">
+      <view class="mb20rpx text-30rpx font-semibold">
+        抵扣信息
+      </view>
+      <view class="text-26rpx text-#666 leading-48rpx">
+        <view>抵扣券:{{ oilInfo.allowanceId || '-' }}</view>
+        <view>优惠描述:{{ oilInfo.activityName || '-' }}</view>
+      </view>
+    </view>
+
+    <!-- 支付信息(已支付/已退款) -->
+    <view v-if="orderInfo.oilOrderStatus === 2 || orderInfo.oilOrderStatus === 6" class="mx24rpx mt20rpx rounded-16rpx bg-white px28rpx py32rpx">
+      <view class="mb20rpx text-30rpx font-semibold">
+        支付信息
+      </view>
+      <view class="text-26rpx text-#666 leading-48rpx">
+        <view>支付方式:微信支付</view>
+        <view>支付状态:成功</view>
+        <view class="break-all">
+          支付流水号:{{ orderInfo.transactionId || '-' }}
+        </view>
+      </view>
+    </view>
+
+    <!-- 开票信息(已支付) -->
+    <view v-if="orderInfo.oilOrderStatus === 2" class="mx24rpx mt20rpx rounded-16rpx bg-white px28rpx py32rpx">
+      <view class="mb20rpx text-30rpx font-semibold">
+        开票信息
+      </view>
+      <view class="text-26rpx text-#666 leading-48rpx">
+        <view>开票方式:线上自助开票</view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+:deep(.cancel-btn) {
+  border-color: var(--them-color) !important;
+  color: var(--them-color) !important;
+}
+:deep(.wd-count-down){
+  color: #FF4A39 !important;
+}
+</style>

+ 2 - 2
src/subPack-refueling/webView/index.vue

@@ -3,10 +3,10 @@ definePage({
   name: 'refueling-webview',
   islogin: false,
   style: {
-    navigationBarTitleText: '支付',
+    navigationBarTitleText: '',
   },
 })
-const url = ref('https://static.am.xiaojukeji.com/cf-terminal/oil/thanos-fe-oil/pages/open-energy-h5-pay/index.html?orderId=3521815090950710849&tradeId=20260130e21328dee081108086896a3c4e630e9926408709&redirectUrl=')
+const url = ref('https://smqjh.ddjy.zswlgz.com')
 </script>
 
 <template>

+ 17 - 0
src/subPack-smqjh/bannerDetaile/index.vue

@@ -0,0 +1,17 @@
+<script setup lang="ts">
+import { StaticUrl } from '@/config'
+
+definePage({ name: 'bannerDetaile', islogin: false, style: { navigationBarTitleText: '详情' } })
+</script>
+
+<template>
+  <view class="box-border w-full p24rpx">
+    <image
+      :src="`${StaticUrl}/smqjh-dt.jpg`"
+      class="w-full"
+      mode="widthFix"
+    />
+  </view>
+</template>
+
+<style lang="scss" scoped></style>

+ 85 - 0
src/subPack-smqjh/components/attractions-orderList/attractions-orderList.vue

@@ -0,0 +1,85 @@
+<script setup lang="ts">
+import router from '@/router'
+
+const props = defineProps<{
+  order: Api.xsbOrderList
+}>()
+
+// 订单状态映射(hbOrderStatus)
+const statusMap: Record<number, { text: string, color: string }> = {
+  0: { text: '待支付', color: '#FF9500' },
+  60: { text: '已取消', color: '#999999' },
+  70: { text: '已支付', color: '#52C41A' },
+  80: { text: '已完成', color: '#52C41A' },
+}
+
+// 订单项点击
+function handleOrderClick(order: Api.ScenicOrderListVo | Api.xsbOrderList) {
+  router.push({ name: 'attractions-order-detail', params: { orderNo: String(order.orderNumber) } })
+}
+const statusInfo = computed(() => {
+  const status = props.order?.hbOrderStatus as number
+  return statusMap[status] ?? { text: '', color: '#999999' }
+})
+</script>
+
+<template>
+  <view class="mb-20rpx box-border w-full overflow-hidden rounded-16rpx bg-#FFF p-24rpx" @click="handleOrderClick(order)">
+    <view class="w-full flex items-center justify-between">
+      <view class="text-32rpx font-bold">
+        {{ order?.orderScenic.viewName }}
+      </view>
+      <view class="text-28rpx" :style="{ color: statusInfo.color }">
+        {{ statusInfo.text }}
+      </view>
+    </view>
+    <view class="mt-20rpx w-full flex items-center gap-20rpx">
+      <image v-if="order?.orderScenic.img" :src="order?.orderScenic.img" class="h-160rpx w-160rpx flex-shrink-0 rounded-8rpx" mode="aspectFill" />
+      <view v-else class="h-160rpx w-160rpx flex flex-shrink-0 items-center justify-center rounded-8rpx bg-#f5f5f5 text-#999">
+        暂无图片
+      </view>
+      <view class="min-w-0 flex-1">
+        <view class="line-clamp-2 text-28rpx font-bold">
+          {{ order?.orderScenic.productName }}
+        </view>
+        <view class="mt-20rpx text-24rpx text-#666">
+          游玩日期:{{ order?.orderScenic.travelDate }}
+        </view>
+      </view>
+    </view>
+    <view class="mt-24rpx flex items-center justify-between text-24rpx text-#999">
+      <view>订单编号:{{ order?.orderNumber }}</view>
+      <view>共{{ order?.orderScenic.num }}件商品</view>
+    </view>
+    <view class="mt-20rpx flex items-center justify-between">
+      <view class="text-24rpx text-#999">
+        下单时间:{{ order?.createTime }}
+      </view>
+      <view>
+        <text class="text-24rpx">
+          合计:
+        </text>
+        <text class="text-26rpx text-#FF4A39 font-bold">
+          ¥
+        </text>
+        <text class="text-bold text-32rpx text-#FF4A39">
+          {{ order?.total }}
+        </text>
+      </view>
+    </view>
+    <view v-if="order?.hbOrderStatus === 0" class="my-24rpx flex items-center justify-end">
+      <view class="mr-20rpx">
+        <wd-button size="small" plain type="info">
+          取消订单
+        </wd-button>
+      </view>
+      <view class="mr-20rpx">
+        <wd-button size="small" plain type="error">
+          付款
+        </wd-button>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped></style>

+ 1 - 1
src/subPack-smqjh/components/charge-orderList/charge-orderList.vue

@@ -30,7 +30,7 @@ function handleItemClick(item: Api.xsbOrderList) {
 </script>
 
 <template>
-  <view class="mt-20rpx mt20rpx rounded-16rpx bg-#FFF p-24rpx" @click="handleItemClick(order)">
+  <view class="mb-20rpx rounded-16rpx bg-#FFF p-24rpx" @click="handleItemClick(order)">
     <view class="flex items-center justify-between">
       <view>
         <view class="text-28rpx font-bold">

+ 1 - 1
src/subPack-smqjh/components/film-orderList/film-orderList.vue

@@ -127,7 +127,7 @@ function handleOrder(orderNo: string) {
   background: #FFFFFF;
   border-radius: 16rpx 16rpx 16rpx 16rpx;
   padding: 24rpx;
-  margin-top: 20rpx;
+  margin-bottom: 20rpx;
 }
 
   .order-item {

+ 138 - 0
src/subPack-smqjh/components/refueling-order/index.vue

@@ -0,0 +1,138 @@
+<script setup lang="ts">
+import router from '@/router'
+
+defineProps<{
+  order: Api.xsbOrderList
+}>()
+
+const _emit = defineEmits<{
+  refresh: []
+}>()
+// 订单状态映射
+const statusMap = new Map([
+  [1, '待支付'],
+  [2, '已支付'],
+  [6, '已退款'],
+  [9, '已取消'],
+])
+
+// 获取状态文字
+function getStatusText(status?: number) {
+  return statusMap.get(status as number) || ''
+}
+// 计算倒计时剩余毫秒数(30分钟)
+function getCountdownTime(expireTime?: string): number {
+  if (!expireTime)
+    return 0
+  const expire = new Date(expireTime).getTime()
+  const remaining = expire - Date.now()
+  return remaining > 0 ? remaining : 0
+}
+function handleCancelOrder(order: Api.xsbOrderList) {
+  useGlobalMessage().confirm({
+    title: '提示',
+    msg: '确定要取消该订单吗?',
+    zIndex: 9999,
+    success: async () => {
+      uni.showLoading({ mask: true })
+      try {
+        await Apis.refueling.cancelOrder({ params: { orderId: String(order.orderNumber) } })
+        useGlobalToast().show('取消成功')
+        _emit('refresh')
+      }
+      catch {
+        useGlobalToast().show('取消失败')
+      }
+      finally {
+        uni.hideLoading()
+      }
+    },
+  })
+}
+</script>
+
+<template>
+  <view class="mb-20rpx rounded-16rpx bg-white p-24rpx">
+    <view @click="router.push({ name: 'refueling-orderDetail', params: { orderNo: order.orderNumber as string } })">
+      <!-- 头部:油站名称 + 状态 -->
+      <view class="flex justify-between">
+        <view class="text-30rpx font-semibold">
+          {{ order.omsOrderOilVO?.brandName ? `${order.omsOrderOilVO?.brandName}·` : '' }}{{ order.omsOrderOilVO?.storeName }}
+        </view>
+        <!-- 待支付状态显示倒计时 -->
+        <view v-if="order?.oilOrderStatus === 1" class="w300rpx flex items-center justify-end text-24rpx text-#FF4D3A">
+          待支付(<wd-count-down :time="getCountdownTime(order?.expireTime)" format="还剩mm:ss" />)
+        </view>
+        <view v-else class="text-24rpx text-#666">
+          {{ getStatusText(order?.oilOrderStatus) }}
+        </view>
+      </view>
+
+      <!-- 油号 | 油枪 -->
+      <view class="mt16rpx text-26rpx text-#999">
+        {{ order.omsOrderOilVO?.itemName }} | {{ order.omsOrderOilVO?.gunNo }}号枪
+      </view>
+
+      <!-- 订单金额 -->
+      <view class="mt12rpx text-26rpx text-#999">
+        订单金额:<text class="text-#333">
+          ¥{{ order.omsOrderOilVO?.totalMoney }}
+        </text>
+      </view>
+
+      <!-- 根据状态显示不同金额 -->
+      <view v-if="order?.oilOrderStatus === 1" class="mt12rpx text-26rpx text-#999">
+        需付金额:<text class="text-#333">
+          ¥{{ order.omsOrderOilVO?.realMoney }}
+        </text>
+      </view>
+      <view v-else-if="order?.oilOrderStatus === 2" class="mt12rpx text-26rpx text-#999">
+        实付金额:<text class="text-#333">
+          ¥{{ order.omsOrderOilVO?.realMoney }}
+        </text>
+      </view>
+      <view v-else-if="order?.oilOrderStatus === 6" class="mt12rpx text-26rpx text-#999">
+        退款金额:<text class="text-#333">
+          ¥{{ order.omsOrderOilVO?.realMoney }}
+        </text>
+      </view>
+
+      <!-- 根据状态显示不同时间 -->
+      <view v-if="order?.oilOrderStatus === 1" class="mt12rpx text-26rpx text-#999">
+        下单时间:{{ order?.createTime }}
+      </view>
+      <view v-else-if="order?.oilOrderStatus === 2" class="mt12rpx text-26rpx text-#999">
+        支付时间:{{ order.omsOrderOilVO?.payTime }}
+      </view>
+      <view v-else-if="order?.oilOrderStatus === 6" class="mt12rpx text-26rpx text-#999">
+        退款时间:{{ order.omsOrderOilVO?.refundTime }}
+      </view>
+      <view v-else-if="order?.oilOrderStatus === 9" class="mt12rpx text-26rpx text-#999">
+        取消时间:{{ order?.cancelTime }}
+      </view>
+
+      <!-- 已支付显示合作方订单号 -->
+      <view v-if="order?.oilOrderStatus === 2 && order?.thirdOrderId" class="mt12rpx text-26rpx text-#999">
+        合作方订单号:{{ order?.thirdOrderId }}
+      </view>
+    </view>
+    <!-- 操作按钮 -->
+    <view v-if="order.oilOrderStatus === 1" class="mt20rpx flex items-center justify-end gap-20rpx">
+      <wd-button plain size="small" custom-class="action-btn" @click="handleCancelOrder(order)">
+        取消订单
+      </wd-button>
+      <!-- <wd-button plain size="small" custom-class="action-btn" @click="handlePay(order)">
+        付款
+      </wd-button> -->
+    </view>
+    <!-- <view v-else-if="order.oilOrderStatus === 2" class="mt20rpx flex items-center justify-end">
+      <wd-button plain size="small" custom-class="action-btn" @click="handleInvoice(order)">
+        申请开票
+      </wd-button>
+    </view> -->
+  </view>
+</template>
+
+<style lang="scss" scoped>
+
+</style>

+ 1 - 1
src/subPack-smqjh/components/videoRights-orderList/videoRights-orderList.vue

@@ -21,7 +21,7 @@ function handleItemClick(item: Api.xsbOrderList) {
 </script>
 
 <template>
-  <view class="mb-28rpx rounded-16rpx bg-#FFF p-24rpx" @click="handleItemClick(order)">
+  <view class="mb-20rpx rounded-16rpx bg-#FFF p-24rpx" @click="handleItemClick(order)">
     <view class="flex items-center justify-between">
       <view class="text-28rpx">
         {{ order.createTime }}

+ 55 - 3
src/subPack-smqjh/components/xsb-orderList/xsb-orderList.vue

@@ -9,7 +9,10 @@ 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,10 +49,35 @@ async function handleAfterSale(item: Api.xsbOrderList) {
   }
   await useSysStore().getRefunOrder(item.orderNumber as string)
 }
+
+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>
 
 <template>
-  <view class="mt-20rpx rounded-16rpx bg-white p-24rpx">
+  <view class="mb-20rpx rounded-16rpx bg-white p-24rpx">
     <view class="flex items-center justify-between">
       <view class="flex items-center">
         <image :src="`${StaticUrl}/order-icon.png`" class="h-36rpx w-36rpx" />
@@ -63,7 +91,7 @@ async function handleAfterSale(item: Api.xsbOrderList) {
         </template>
         <template v-else>
           <view class="flex items-center">
-            待支付( 还剩 <wd-count-down :time="useUserStore().handleXSBCommonOrderStatusText(order)" @finish="$emit('refresh')">
+            待支付( 还剩 <wd-count-down :time="useUserStore().handleXSBCommonOrderStatusText(order)">
               <template #default="{ current }">
                 <view class="text-24rpx text-[#FF4D3A]">
                   {{ current.minutes }}:{{ current.seconds }}
@@ -140,14 +168,38 @@ async function handleAfterSale(item: Api.xsbOrderList) {
           </wd-button>
         </view>
       </template>
-      <template v-if="order.refundStatus != 2 && [OrderStatus.OrderCompleted, OrderStatus.OrderWaitDelivery, OrderStatus.OrderAccepted].includes(order.hbOrderStatus) ">
+      <template v-if="order.refundStatus != 2 && [OrderStatus.OrderWaitDelivery, OrderStatus.OrderAccepted].includes(order.hbOrderStatus) ">
         <view class="mr-20rpx">
           <wd-button size="small" plain type="info" @click.stop="() => handleAfterSale(order)">
             申请售后
           </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>
 

+ 7 - 0
src/subPack-smqjh/order/components/OrderRenderer.vue

@@ -4,6 +4,8 @@ import xsbList from '../../components/xsb-orderList/xsb-orderList.vue'
 import filmList from '../../components/film-orderList/film-orderList.vue'
 import djkorder from '../../components/djk-order/index.vue'
 import videoRightsList from '../../components/videoRights-orderList/videoRights-orderList.vue'
+import Jy from '../../components/refueling-order/index.vue'
+import attractionsList from '../../components/attractions-orderList/attractions-orderList.vue'
 
 interface Props {
   orderList: Api.xsbOrderList
@@ -32,5 +34,10 @@ defineEmits<{
       @refresh="$emit('refresh')"
     />
     <videoRightsList v-else-if="orderList.businessType === 'XNSP' || orderList.businessType === 'all'" :order="orderList" />
+    <Jy v-else-if="orderList.businessType === 'JY' || orderList.businessType === 'all'" :order="orderList" @refresh="$emit('refresh')" />
+    <attractionsList
+      v-else-if="orderList.businessType === 'JDMP' || orderList.businessType === 'all'" :order="orderList"
+      @refresh="$emit('refresh')"
+    />
   </block>
 </template>

+ 57 - 8
src/subPack-smqjh/order/index.vue

@@ -14,6 +14,14 @@ const skelet = ref(true)
 const navActiveTab = ref('all')
 const scrollViewId = ref()
 const orderStatusActive = ref('all')
+
+const orderCache = new Map<string, Api.xsbOrderList[]>()
+
+const tabLastPageMap = new Map<string, boolean>()
+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,28 +36,69 @@ 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
+  const key = getCacheKey()
+  orderCache.set(key, [...orderList.value])
+  setTimeout(() => {
+    tabLastPageMap.set(key, isLastPage.value)
+  }, 100)
+})
 
+function loadTab() {
+  const key = getCacheKey()
+  if (orderCache.has(key)) {
+    orderList.value = orderCache.get(key)!
+    skelet.value = false
+    uni.pageScrollTo({ scrollTop: 0, duration: 0 })
+  }
+  else {
+    tabLastPageMap.delete(key)
+    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 的所有状态,强制刷新
+  const key = getCacheKey()
+  orderCache.delete(key)
+  tabLastPageMap.delete(key)
   reload()
 })
+
 onReachBottom(() => {
-  if (!isLastPage.value) {
+  const key = getCacheKey()
+  // 优先使用 per-tab 的 isLastPage,避免从其他 tab 继承错误状态
+  const tabIsLast = tabLastPageMap.has(key) ? tabLastPageMap.get(key) : isLastPage.value
+  if (!tabIsLast) {
     page.value++
   }
 })
 function handleChangeStatus(value: string) {
-  skelet.value = true
   orderStatusActive.value = value
+  loadTab()
+}
+
+// 子组件触发刷新时,清除当前 tab 的所有状态再重新加载
+function handleRefresh() {
+  const key = getCacheKey()
+  orderCache.delete(key)
+  tabLastPageMap.delete(key)
+  skelet.value = true
   orderList.value = []
   reload()
 }
@@ -134,7 +183,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="暂无内容" />

+ 2 - 1
src/subPack-smqjh/order/order-data.ts

@@ -5,7 +5,8 @@ export const navTabTypeList = [
   { name: '电影演出', value: 'DYY' },
   { name: '视频权益', value: 'XNSP' },
   { name: '大健康', value: 'DJK' },
-  { name: '大牌点餐', value: 'SHOP' },
+  { name: '加油', value: 'JY' },
+  { name: '景区', value: 'JDMP' },
 ]
 
 export const orderStatusList = [

+ 16 - 5
src/subPack-videoRights/commonTab/components/home.vue

@@ -56,16 +56,17 @@ function clearSearch() {
     <view class="box-border px24rpx">
       <scroll-view scroll-y type="custom">
         <grid-view type="masonry" cross-axis-count="2" main-axis-gap="10" cross-axis-gap="10">
-          <view v-for="item in videoDataList" :key="item?.id" class="mt-18rpx rounded-16rpx bg-#FFF" @click="router.push({ name: 'video-rights-detail', params: { id: item.id } })">
-            <view>
+          <view v-for="item in videoDataList" :key="item?.id" class="mt-18rpx rounded-16rpx bg-#FFF" @click="router.push({ name: 'video-rights-detail', params: { id: item.id || '' } })">
+            <view class="image-wrapper h-342rpx w-342rpx">
               <image
-                class="h-342rpx w-342rpx rounded-16rpx"
+                class="h-full w-full rounded-16rpx object-c"
+                mode="aspectFill"
                 :src="item.productImg ? item.productImg : `${StaticUrl}/list-page-img.png`"
               />
             </view>
             <view class="mx20rpx my-24rpx">
               <view class="text-28rpx font-bold">
-                {{ item.productName }}
+                {{ item.localProductName ? item.localProductName : item.productName }}
               </view>
               <view class="mt-30rpx flex items-center justify-between">
                 <view class="text-#FF4D3A">
@@ -90,4 +91,14 @@ function clearSearch() {
   </view>
 </template>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+.image-wrapper {
+  overflow: hidden;
+}
+.image-wrapper .object-c {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+  display: block;
+}
+</style>

+ 2 - 2
src/subPack-videoRights/commonTab/components/order.vue

@@ -50,12 +50,12 @@ function handleClick(e: any) {
       safe-area-inset-top fixed
     />
     <view :style="{ paddingTop: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 12}px` }" />
-    <wd-tabs v-model="tab" animated @click="handleClick">
+    <wd-tabs v-model="tab" :offset-top="statusBarHeight + MenuButtonHeight + 12" animated @click="handleClick">
       <block v-for="item in tabsList" :key="item">
         <wd-tab :title="item">
           <view class="box-border bg-#f6f6f6 px24rpx">
             <view class="h-4rpx" />
-            <view v-for=" order in orderList" :key="order.orderNumber" class="mt-28rpx rounded-16rpx bg-#FFF p-24rpx" @click="router.push({ name: 'video-rights-order-info', params: { orderNo: order.orderNumber } })">
+            <view v-for=" order in orderList" :key="order.orderNumber" class="mt-28rpx rounded-16rpx bg-#FFF p-24rpx" @click="router.push({ name: 'video-rights-order-info', params: { orderNo: order.orderNumber || '' } })">
               <view class="flex items-center justify-between">
                 <view class="text-28rpx">
                   {{ order.createTime }}

+ 1 - 1
src/subPack-videoRights/videoRightsDetail/videoRightsDetail.vue

@@ -27,7 +27,7 @@ async function getGoodsDetail() {
 <template>
   <view class="video-rights-detail-page">
     <view class="relative h-750rpx">
-      <image class="h-750rpx w-full" :src="`${StaticUrl}/videoRight-goodsDetail.png`" />
+      <image class="h-750rpx w-full" :src="goodsDetail?.productImg ? goodsDetail?.productImg : `${StaticUrl}/videoRight-goodsDetail.png`" />
       <view class="absolute bottom-0 left-24rpx right-24rpx rounded-16rpx bg-#FFF p-24rpx">
         <view class="flex items-center gap-18rpx">
           <view class="text-#FF4D3A">

+ 25 - 1
src/subPack-videoRights/videoRightsOrderInfo/videoRightsOrderInfo.vue

@@ -3,17 +3,21 @@ import StatusDisplay from '../components/StatusDisplay.vue'
 import { StaticUrl } from '@/config'
 import router from '@/router'
 
+const { statusBarHeight, MenuButtonHeight } = storeToRefs(useSysStore())
 definePage({
   name: 'video-rights-order-info',
   islogin: true,
   style: {
     navigationBarTitleText: '订单确认',
+    navigationStyle: 'custom',
   },
 })
 
 const orderNo = ref('')
+const topUp = ref('')
 onLoad((options: any) => {
   orderNo.value = options.orderNo
+  topUp.value = options.topUp
   console.log(options, '页面数据')
 })
 onMounted(() => {
@@ -25,9 +29,29 @@ async function getOrderDetail() {
   const res = await Apis.videoRight.orderDetail({ data: { orderNumber: orderNo.value } })
   videoRightOrderDetail.value = res.data
 }
+
+function orderBack() {
+  const vrIndex = 'subPack-videoRights/commonTab/index'
+  const pages = getCurrentPages()
+  const targetPageIndex = pages.findIndex(page => page.route === vrIndex)
+  if (topUp.value === 'true') {
+    if (targetPageIndex !== -1) {
+      const delta = pages.length - targetPageIndex - 1
+      router.back({ delta, animationType: 'fade-out' })
+    }
+  }
+  else {
+    router.back()
+  }
+}
 </script>
 
 <template>
+  <wd-navbar
+    title="订单详情" custom-style="background-color:#FFF" :bordered="false" :z-index="99"
+    safe-area-inset-top left-arrow fixed @click-left="orderBack"
+  />
+  <view :style="{ paddingTop: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 12}px` }" />
   <view class="video-rights-order-info-page">
     <view class="box-border px24rpx">
       <view class="h-22rpx" />
@@ -100,7 +124,7 @@ async function getOrderDetail() {
       </view>
     </view>
     <view class="fixed bottom-0 h-174rpx w-full border-[1rpx_solid_#EEEEEE] bg-#FFF px-24rpx">
-      <wd-button custom-class="w-702rpx mt-10rpx" block size="large" @click="router.back()">
+      <wd-button custom-class="w-702rpx mt-10rpx" block size="large" @click="orderBack">
         返回
       </wd-button>
     </view>

+ 1 - 1
src/subPack-videoRights/videoRightsSubmitOrder/videoRightsSubmitOrder.vue

@@ -79,7 +79,7 @@ function submitPay() {
       useGlobalLoading().loading({ msg: '提交中...' })
       const res = await Apis.videoRight.orderCreate({ data: submitFrom.value })
       useGlobalLoading().close()
-      router.push({ name: 'video-rights-order-info', params: { orderNo: res.data } })
+      router.push({ name: 'video-rights-order-info', params: { orderNo: res.data, topUp: 'true' } })
     },
   })
 }

+ 83 - 22
src/subPack-xsb/commonTab/components/cart.vue

@@ -7,6 +7,7 @@ const emit = defineEmits(['changeTab'])
 
 const { cartList, isCartAllChecked, totalProduct } = storeToRefs(useSmqjhCartStore())
 const cartStore = useSmqjhCartStore()
+const priceDetailPopup = ref(false)
 
 watch(() => cartList.value, async () => {
   cartStore.getCartTotalPrice()
@@ -34,11 +35,20 @@ onMounted(async () => {
     <view class="xsb-linear h406rpx" />
     <view class="-mt220rpx">
       <view class="content px24rpx">
+        <view v-if="cartList.length" class="mb20rpx flex items-center justify-end">
+          <view class="text-24rpx text-[#AAAAAA]" @click="cartStore.cartDeleteGoods">
+            <wd-icon name="delete" size="24rpx" /> 清空购物车
+          </view>
+        </view>
         <scroll-view scroll-y class="content">
           <view v-for="shop in cartList" :key="shop.shopId" class="mb24rpx rounded-16rpx bg-white px24rpx pb18rpx pt28rpx">
             <wd-checkbox v-model="shop.AllShopGoods" size="large" @change="cartStore.cartStoreAllChecked($event, shop)">
               <view class="text-28rpx font-semibold">
-                {{ shop.shopName }}
+                {{ shop.shopName }} <text class="text-24rpx text-#AAAAAA">
+                  已选 {{ shop.allGoods.length }} 件<text v-if="shop.allGoods.length">
+                    ,总重量约为 {{ totalProduct?.totalWeight || 0 }} kg
+                  </text>
+                </text>
               </view>
             </wd-checkbox>
             <view class="mt20rpx h2rpx w-full bg-#F0F0F0" />
@@ -128,31 +138,31 @@ onMounted(async () => {
     <view v-if="cartList.length" class="fixedShadow fixed bottom-60rpx left-0 z-99 box-border w-full flex items-center justify-between rounded-t-16rpx bg-white px24rpx pb60rpx pt10rpx">
       <view class="ios w-full flex items-center justify-between">
         <view class="flex items-center">
-          <image
-            :src="`${StaticUrl}/cart-lanzi.png`"
-            class="h100rpx w100rpx"
-          />
-          <view class="ml16rpx flex items-center">
-            <wd-checkbox v-model="isCartAllChecked" size="large" @change="cartStore.cartAllChecked">
-              全选
-            </wd-checkbox>
-            <view class="ml10rpx text-24rpx text-#FF4A39" @click="cartStore.cartDeleteGoods">
-              删除
-            </view>
+          <view class="flex items-center">
+            <!-- <wd-badge :model-value="getTotalNum" :top="20"> -->
+            <image :src="`${StaticUrl}/cart-lanzi.png`" class="h100rpx w100rpx" />
+            <!-- </wd-badge> -->
           </view>
-        </view>
-        <view class="flex items-center">
-          <view class="flex items-center font-semibold">
-            <view class="text-22rpx text-#222">
-              总计:
+          <view class="ml40rpx">
+            <view class="flex items-center">
+              <view class="font-semibold">
+                ¥ {{ totalProduct?.amount || 0 }}
+              </view>
+              <view v-if="totalProduct?.coupon" class="ml10rpx text-24rpx text-#FF4A39">
+                共减¥{{ totalProduct?.coupon }}
+              </view>
+              <view v-if="totalProduct?.amount" class="ml10rpx flex items-center text-24rpx text-gray" @click="priceDetailPopup = !priceDetailPopup">
+                明细 <view class="flex items-center" :class="[priceDetailPopup ? 'rotate-180' : '']">
+                  <wd-icon name="arrow-up" size="24rpx" color="#aaa" />
+                </view>
+              </view>
             </view>
-            <view class="flex items-baseline text-24rpx text-#FF4A39">
-              ¥
-              <text class="text-36rpx">
-                {{ totalProduct?.price || '0.00' }}
-              </text>
+            <view class="mt10rpx text-24rpx text-gray">
+              配送费:¥{{ totalProduct?.transfee || 0 }}
             </view>
           </view>
+        </view>
+        <view class="flex items-center">
           <view class="ml20rpx w160rpx">
             <wd-button block size="large" @click="cartStore.cartOrderConfirm">
               结算
@@ -161,6 +171,57 @@ onMounted(async () => {
         </view>
       </view>
     </view>
+    <!-- 价格明细弹窗 -->
+    <Zpopup v-model="priceDetailPopup" :zindex="10" bg="#fff">
+      <view class="ios box-border w-full px-40rpx pb-60rpx pt-36rpx">
+        <view class="mb-40rpx text-center text-32rpx font-semibold">
+          价格明细
+        </view>
+        <view class="flex items-center justify-between py-20rpx">
+          <view class="text-30rpx font-semibold">
+            商品合计
+          </view>
+          <view class="text-30rpx font-semibold">
+            ¥{{ totalProduct?.amount }}
+          </view>
+        </view>
+        <view class="flex items-center justify-between py-20rpx">
+          <view class="text-28rpx text-#333">
+            商品总价
+          </view>
+          <view class="text-28rpx text-#333">
+            ¥{{ totalProduct?.price }}
+          </view>
+        </view>
+        <template v-if="totalProduct?.coupon">
+          <view class="flex items-center justify-between py-20rpx">
+            <view class="text-28rpx text-#333">
+              下单用券共减
+            </view>
+            <view class="text-28rpx text-#FF4A39 font-semibold">
+              -¥{{ totalProduct?.coupon }}
+            </view>
+          </view>
+          <view v-if="totalProduct?.activityName || totalProduct?.couponName" class="flex items-center justify-between pb-20rpx">
+            <view class="text-24rpx text-#AAAAAA">
+              {{ totalProduct?.activityName || totalProduct?.couponName }}
+            </view>
+            <view class="text-24rpx text-#AAAAAA">
+              -¥{{ totalProduct?.coupon }}
+            </view>
+          </view>
+        </template>
+        <view class="flex items-center justify-between py-20rpx">
+          <view class="text-28rpx text-#333">
+            配送费
+          </view>
+          <view class="text-28rpx text-#333">
+            ¥{{ totalProduct?.transfee }}
+          </view>
+        </view>
+      </view>
+      <view class="h200rpx" />
+    </Zpopup>
   </view>
 </template>
 

+ 318 - 118
src/subPack-xsb/commonTab/components/classfiy.vue

@@ -8,11 +8,53 @@ const props = defineProps<{ categoryList: Api.xsbCategories[], hotText: Api.xsbS
 const { statusBarHeight, MenuButtonHeight } = storeToRefs(useSysStore())
 const { topNavActive, leftActive, SelectShopInfo } = storeToRefs(useSysXsbStore())
 const { userInfo, token } = storeToRefs(useUserStore())
+const { getShopTotalNum } = storeToRefs(useSmqjhCartStore())
 const classfiylist = computed(() => props.categoryList)
 const sortClassBtn = ref(1)
 const show = ref(false)
 const cartList = ref<Api.xsbCategoriesCartList[]>([])
+const cartPopup = ref(false)
+const totalProduct = ref<Api.shoppingCartOrderConfirm>()
+const priceDetailPopup = ref(false)
 const cartIds = computed(() => cartList.value.filter(it => it.isDelete !== '1' && it.shopSkuStocks !== '0').map(it => it.id))
+// 勾选状态:只计算勾选商品的价格
+const selectedIds = ref<number[]>([])
+const isAllSelected = computed(() => cartIds.value.length > 0 && cartIds.value.every(id => selectedIds.value.includes(id)))
+const selectedCount = computed(() => selectedIds.value.length)
+
+function toggleSelectAll() {
+  if (isAllSelected.value) {
+    selectedIds.value = []
+  }
+  else {
+    selectedIds.value = [...cartIds.value]
+  }
+  getGoodsPrice()
+}
+async function handleClearCart() {
+  useGlobalMessage().confirm({
+    title: '提示',
+    msg: '确认清空购物车?',
+    success: async () => {
+      uni.showLoading({ mask: true })
+      try {
+        const ids = cartList.value.map(it => it.id).join(',')
+        if (ids) {
+          await Apis.common.deleteShoppingCart({ pathParams: { ids } })
+        }
+        cartPopup.value = false
+        selectedIds.value = []
+        totalProduct.value = undefined
+        useSmqjhCartStore().getCartList('XSB')
+        await getCartCategorList()
+        setProductNum()
+      }
+      finally {
+        uni.hideLoading()
+      }
+    },
+  })
+}
 const showball = ref(false)
 const _this = getCurrentInstance()
 const selectGoods = ref(false)
@@ -23,7 +65,6 @@ const goodsInTo = ref()
 const goodsInfo = ref<Api.xsbCategoryProductList | Api.xsbProductDetail | undefined>()
 
 const isTopLoading = ref(false)
-const cartPopup = ref(false)
 const basllObj = ref({
   left: 0,
   top: 0,
@@ -42,7 +83,6 @@ const goodsLoading = ref<LoadMoreState>()
 const navHeight = computed(() => {
   return (`${Number(MenuButtonHeight.value) + Number(statusBarHeight.value)}px`)
 })
-const totalProduct = ref<Api.shoppingCartOrderConfirm>()
 function handleTopNavChange(item: Api.xsbCategoriesChildren) {
   topNavActive.value = item.code
   if (!item.children)
@@ -73,9 +113,7 @@ async function getCartCategorList() {
       },
     }).then((res) => {
       cartList.value = res.data
-      if (cartList.value.length) {
-        getGoodsPrice()
-      }
+      console.log(cartList.value, '===================')
       resolve(res)
     }).catch((err) => {
       reject(err)
@@ -162,42 +200,52 @@ async function handleAddCart(event: WechatMiniprogram.TouchEvent, item: Api.xsbC
   setTimeout(() => {
     showball.value = false
   }, 500)
-  await useSmqjhCartStore().addCart(item.skuList[0].skuId, 1, Number(item.shopId), 'XSB')
+  const skuId = item.skuList[0].skuId
+  await useSmqjhCartStore().addCart(skuId, 1, Number(item.shopId), 'XSB')
   await getCartCategorList()
   setProductNum()
+  // 自动选中刚加入的商品,并重新计算价格
+  const cartItem = cartList.value.find(it => it.skuId === skuId && it.isDelete !== '1' && it.shopSkuStocks !== '0')
+  if (cartItem && !selectedIds.value.includes(cartItem.id)) {
+    selectedIds.value.push(cartItem.id)
+  }
+  await getGoodsPrice()
 }
 async function handleSubCart(event: WechatMiniprogram.TouchEvent, item: Api.xsbCategoryProductList) {
+  const skuId = item.skuList[0].skuId
   if (item.num === 1) {
     useGlobalMessage().confirm({
       msg: '是否删除该商品?',
       success: async () => {
-        await useSmqjhCartStore().addCart(item.skuList[0].skuId, -1, Number(item.shopId), 'XSB')
+        // 删除前记录 cartId,用于从 selectedIds 中移除
+        const cartItem = cartList.value.find(it => it.skuId === skuId)
+        await useSmqjhCartStore().addCart(skuId, -1, Number(item.shopId), 'XSB')
         await getCartCategorList()
         setProductNum()
-        totalProduct.value = undefined
+        if (cartItem) {
+          selectedIds.value = selectedIds.value.filter(id => id !== cartItem.id)
+        }
+        await getGoodsPrice()
       },
     })
   }
   else {
-    if (showball.value)
-      return
-    basllObj.value.left = event.detail.x
-    basllObj.value.top = event.detail.y
-    showball.value = true
-    setTimeout(() => {
-      showball.value = false
-    }, 500)
-    await useSmqjhCartStore().addCart(item.skuList[0].skuId, -1, Number(item.shopId), 'XSB')
+    await useSmqjhCartStore().addCart(skuId, -1, Number(item.shopId), 'XSB')
     await getCartCategorList()
     setProductNum()
+    await getGoodsPrice()
   }
 }
 
 onMounted(async () => {
   if (!topNavActive.value || !leftActive.value) {
-    topNavActive.value = props.categoryList && props.categoryList[0].code
-    leftActive.value = props.categoryList[0].children && props.categoryList[0].children[0].code
+    const firstWithChildren = props.categoryList?.find(it => it.children && it.children.length > 0)
+    if (firstWithChildren) {
+      topNavActive.value = firstWithChildren.code
+      leftActive.value = firstWithChildren.children![0].code
+    }
   }
+
   goodsLoading.value = 'loading'
   if (leftActive.value) {
     handleChange({ value: leftActive.value })
@@ -211,7 +259,7 @@ onMounted(async () => {
       topScrollView.value = topNavActive.value
     })
   }
-  console.log(topNavActive.value, '  ==', leftActive.value)
+  console.log(topNavActive.value, '  ==', props.categoryList)
 
   getCartBox()
   if (token.value) {
@@ -239,10 +287,13 @@ function handleGo(item: Api.xsbCategoryProductList) {
 }
 
 async function getGoodsPrice() {
-  if (cartIds.value.length) {
-    const res = await useSmqjhCartStore().getCartAddGoodsPrice(cartIds.value.join(','))
+  if (selectedIds.value.length) {
+    const res = await useSmqjhCartStore().getCartAddGoodsPrice(selectedIds.value.join(','))
     totalProduct.value = res
   }
+  else {
+    totalProduct.value = undefined
+  }
 }
 async function handleSub(item: Api.xsbCategoriesCartList) {
   if (item.num === 1) {
@@ -329,8 +380,23 @@ function handlePay() {
     useGlobalToast().show({ msg: `${shopSkuStock[0].skuName}库存不足` })
     return
   }
+  cartPopup.value = false
   router.push({ name: 'xsb-confirmOrder', params: { data: JSON.stringify(totalProduct.value) } })
 }
+function handleOpen() {
+  if (!getShopTotalNum.value)
+    return
+  cartPopup.value = true
+  priceDetailPopup.value = false
+}
+</script>
+
+<script lang="ts">
+export default {
+  options: {
+    styleIsolation: 'shared',
+  },
+}
 </script>
 
 <template>
@@ -377,13 +443,16 @@ function handlePay() {
             class="mr24rpx flex flex-col items-center justify-center" @click="handleTopNavChange(item)"
           >
             <view class="relative">
-              <view class="box-border" :class="[topNavActive == item.code ? 'overflow-hidden border-solid border-[var(--them-color)] border-2rpx rounded-26rpx h84rpx w-84rpx' : 'h72rpx w-72rpx']">
-                <image
-                  :src="item.icon"
-                  class="h-full w-full"
-                />
+              <view
+                class="box-border"
+                :class="[topNavActive == item.code ? 'overflow-hidden border-solid border-[var(--them-color)] border-2rpx rounded-26rpx h84rpx w-84rpx' : 'h72rpx w-72rpx']"
+              >
+                <image :src="item.icon" class="h-full w-full" />
               </view>
-              <view v-if="!item.children" class="absolute left-0 top-0 h-full w-full flex items-center justify-center rounded-26rpx bg-[rgba(0,0,0,0.6)] text-16rpx text-white">
+              <view
+                v-if="!item.children"
+                class="absolute left-0 top-0 h-full w-full flex items-center justify-center rounded-26rpx bg-[rgba(0,0,0,0.6)] text-16rpx text-white"
+              >
                 敬请期待
               </view>
             </view>
@@ -442,10 +511,21 @@ function handlePay() {
       </wd-popup>
     </view>
     <view class="wraper">
-      <scroll-view class="w200rpx" :scroll-into-view-offset="-150" enable-passive scroll-with-animation scroll-y :scroll-into-view="`id${leftActive}`">
-        <view v-for="item in categories" :id="`id${item.code}`" :key="item.code" :class="[item.code == leftActive ? 'bg-white' : '']" class="relative h100rpx flex items-center justify-center whitespace-nowrap text-28rpx" @click="handleLeftChange(item)">
+      <scroll-view
+        class="w200rpx" :scroll-into-view-offset="-150" enable-passive scroll-with-animation scroll-y
+        :scroll-into-view="`id${leftActive}`"
+      >
+        <view
+          v-for="item in categories" :id="`id${item.code}`" :key="item.code"
+          :class="[item.code == leftActive ? 'bg-white' : '']"
+          class="relative h100rpx flex items-center justify-center whitespace-nowrap text-28rpx"
+          @click="handleLeftChange(item)"
+        >
           {{ item.name }}
-          <view v-if="item.code == leftActive" class="absolute left-0 top-20rpx h60rpx w8rpx rounded-4rpx bg-[var(--them-color)]" />
+          <view
+            v-if="item.code == leftActive"
+            class="absolute left-0 top-20rpx h60rpx w8rpx rounded-4rpx bg-[var(--them-color)]"
+          />
         </view>
       </scroll-view>
       <view class="content">
@@ -460,23 +540,17 @@ function handlePay() {
         </view>
         <view class="relative">
           <scroll-view
-            :lower-threshold="100"
-            :refresher-triggered="isTopLoading" :scroll-top="scrollTop"
-            :style="{ height: `calc(100vh - ${navHeight} - 700rpx)` }" enable-passive scroll-y scroll-anchoring refresher-enabled :throttle="false"
-            :scroll-into-view="`class${goodsInTo}`"
-
-            @refresherrefresh="handleSrollTop"
-            @scrolltolower="handlScrollBottom"
+            :lower-threshold="100" :refresher-triggered="isTopLoading" :scroll-top="scrollTop"
+            :style="{ height: `calc(100vh - ${navHeight} - 700rpx)` }" enable-passive scroll-y scroll-anchoring
+            refresher-enabled :throttle="false" :scroll-into-view="`class${goodsInTo}`"
+            @refresherrefresh="handleSrollTop" @scrolltolower="handlScrollBottom"
           >
             <view v-if="productList.length" class="p20rpx">
               <view v-for="item in productList" :id="`class${item.prodId}`" :key="item.id" class="relative">
                 <view class="flex" @click="handleGo(item)">
                   <view class="relative mr20rpx h172rpx w172rpx flex-shrink-0 overflow-hidden rounded-16rpx bg-#F6F6F6">
                     <image :src="item.pic" lazy-load class="h-full w-full" />
-                    <image
-                      :src="`${StaticUrl}/xsb-shui-class.png`"
-                      class="absolute left-0 top-0 h-full w-full"
-                    />
+                    <image :src="`${StaticUrl}/xsb-shui-class.png`" class="absolute left-0 top-0 h-full w-full" />
                   </view>
                   <view class="flex-1">
                     <view class="text-left text-28rpx font-semibold">
@@ -514,16 +588,16 @@ function handlePay() {
                       <view v-else>
                         <view class="flex items-center">
                           <image
-                            :src="` ${StaticUrl}/sub-cart.png`"
-                            class="h44rpx w44rpx"
+                            :src="` ${StaticUrl}/sub-cart.png`" class="h44rpx w44rpx"
                             @click.stop="handleSubCart($event, item)"
                           />
-                          <view class="box-border h44rpx w84rpx flex items-center justify-center border border-#F0F0F0 border-solid text-24rpx text-#AAAAAA">
+                          <view
+                            class="box-border h44rpx w84rpx flex items-center justify-center border border-#F0F0F0 border-solid text-24rpx text-#AAAAAA"
+                          >
                             {{ item.num }}
                           </view>
                           <image
-                            :src="` ${StaticUrl}/add-cart.png`"
-                            class="h44rpx w44rpx"
+                            :src="` ${StaticUrl}/add-cart.png`" class="h44rpx w44rpx"
                             @click.stop="handleAddCart($event, item)"
                           />
                         </view>
@@ -534,16 +608,26 @@ function handlePay() {
                 <view class="line">
                   <wd-divider color="#F0F0F0" />
                 </view>
-                <view v-if="!item.spuStock" class="absolute left-0 top-0 z-1 h-full w-full flex items-center bg-[rgba(255,255,255,.6)]">
+                <view
+                  v-if="!item.spuStock"
+                  class="absolute left-0 top-0 z-1 h-full w-full flex items-center bg-[rgba(255,255,255,.6)]"
+                >
                   <view class="h172rpx w172rpx flex items-center justify-center">
-                    <view class="h36rpx w112rpx flex items-center justify-center rounded-16rpx bg-[rgba(0,0,0,.6)] text-28rpx text-white">
+                    <view
+                      class="h36rpx w112rpx flex items-center justify-center rounded-16rpx bg-[rgba(0,0,0,.6)] text-28rpx text-white"
+                    >
                       已售罄
                     </view>
                   </view>
                 </view>
-                <view v-if="!item.skuList.some((it) => it.saleStatus)" class="absolute left-0 top-0 z-1 h-full w-full flex items-center bg-[rgba(255,255,255,.6)]">
+                <view
+                  v-if="!item.skuList.some((it) => it.saleStatus)"
+                  class="absolute left-0 top-0 z-1 h-full w-full flex items-center bg-[rgba(255,255,255,.6)]"
+                >
                   <view class="h172rpx w172rpx flex items-center justify-center">
-                    <view class="h36rpx w112rpx flex items-center justify-center rounded-16rpx bg-[rgba(0,0,0,.6)] text-28rpx text-white">
+                    <view
+                      class="h36rpx w112rpx flex items-center justify-center rounded-16rpx bg-[rgba(0,0,0,.6)] text-28rpx text-white"
+                    >
                       不可售
                     </view>
                   </view>
@@ -553,7 +637,10 @@ function handlePay() {
             <StatusTip v-else tip="暂无内容" />
             <view v-if="goodsLoading == 'finished' || isTopLoading" class="h-40vh" />
           </scroll-view>
-          <view v-if="goodsLoading == 'loading' || isTopLoading" class="absolute left-0 top-0 z-10 h-full w-full flex items-center justify-center bg-white">
+          <view
+            v-if="goodsLoading == 'loading' || isTopLoading"
+            class="absolute left-0 top-0 z-10 h-full w-full flex items-center justify-center bg-white"
+          >
             <wd-loading color="#9ED605" :size="20" />
           </view>
         </view>
@@ -563,11 +650,40 @@ function handlePay() {
       class="fixedShadow fixed bottom-60rpx left-0 z-100 box-border w-full flex items-center justify-between rounded-t-16rpx bg-white px24rpx pb60rpx pt10rpx"
     >
       <view class="ios w-full flex items-center justify-between">
-        <view class="flex items-center" @click="cartPopup = true">
-          <image :src="`${StaticUrl}/cart-lanzi.png`" class="cart-box h100rpx w100rpx" />
+        <view class="flex items-center">
+          <view class="flex items-center" @click="handleOpen">
+            <wd-badge :model-value="getShopTotalNum" :top="20">
+              <image v-if="getShopTotalNum" :src="`${StaticUrl}/cart-lanzi.png`" class="cart-box h100rpx w100rpx" />
+              <image v-else :src="`${StaticUrl}/xsb-cart-disabled.png`" class="cart-box h100rpx w100rpx" />
+            </wd-badge>
+          </view>
+          <view v-if="getShopTotalNum" class="ml40rpx">
+            <view class="flex items-center">
+              <view class="font-semibold">
+                ¥ {{ totalProduct?.amount || 0 }}
+              </view>
+              <view v-if="totalProduct?.coupon" class="ml10rpx text-24rpx text-#FF4A39">
+                共减¥{{ totalProduct?.coupon }}
+              </view>
+              <view
+                v-if="totalProduct?.amount" class="ml10rpx flex items-center text-24rpx text-gray"
+                @click="priceDetailPopup = !priceDetailPopup, cartPopup = false"
+              >
+                明细 <view :class="[priceDetailPopup ? 'rotate-180' : '']" class="flex items-center">
+                  <wd-icon name="arrow-up" size="24rpx" color="#aaa" />
+                </view>
+              </view>
+            </view>
+            <view class="mt10rpx text-24rpx text-gray">
+              配送费:¥{{ totalProduct?.transfee || 0 }}
+            </view>
+          </view>
+          <view v-else class="ml40rpx text-24rpx text-#AAA">
+            您还没有添加商品
+          </view>
         </view>
         <view class="flex items-center">
-          <view class="flex items-center font-semibold">
+          <!-- <view class="flex items-center font-semibold">
             <view class="text-22rpx text-#222">
               总计:
             </view>
@@ -577,86 +693,166 @@ function handlePay() {
                 {{ totalProduct?.price || '0.00' }}
               </text>
             </view>
-          </view>
+          </view> -->
           <view class="ml20rpx w160rpx">
-            <wd-button block size="large" @click="handlePay">
+            <wd-button block size="large" :disabled="!getShopTotalNum" :type="getShopTotalNum ? 'primary' : 'info'" @click="handlePay">
               结算
             </wd-button>
           </view>
         </view>
       </view>
     </view>
+    <!-- 价格明细弹窗 -->
+    <Zpopup v-model="priceDetailPopup" :zindex="10" bg="#fff">
+      <view class="ios box-border w-full px-40rpx pb-60rpx pt-36rpx">
+        <view class="mb-40rpx text-center text-32rpx font-semibold">
+          价格明细
+        </view>
+        <!-- 商品合计 -->
+        <view class="flex items-center justify-between py-20rpx">
+          <view class="text-30rpx font-semibold">
+            商品合计
+          </view>
+          <view class="text-30rpx font-semibold">
+            ¥{{ totalProduct?.amount }}
+          </view>
+        </view>
+        <!-- 商品总价 -->
+        <view class="flex items-center justify-between py-20rpx">
+          <view class="text-28rpx text-#333">
+            商品总价
+          </view>
+          <view class="text-28rpx text-#333">
+            ¥{{ totalProduct?.price }}
+          </view>
+        </view>
+        <!-- 下单用券共减 -->
+        <template v-if="totalProduct?.coupon">
+          <view class="flex items-center justify-between py-20rpx">
+            <view class="text-28rpx text-#333">
+              下单用券共减
+            </view>
+            <view class="text-28rpx text-#FF4A39 font-semibold">
+              -¥{{ totalProduct?.coupon }}
+            </view>
+          </view>
+          <!-- 券名称子行 -->
+          <view v-if="totalProduct?.activityName || totalProduct?.couponName" class="flex items-center justify-between pb-20rpx">
+            <view class="text-24rpx text-#AAAAAA">
+              {{ totalProduct?.activityName || totalProduct?.couponName }}
+            </view>
+            <view class="text-24rpx text-#AAAAAA">
+              -¥{{ totalProduct?.coupon }}
+            </view>
+          </view>
+        </template>
+        <!-- 配送费 -->
+        <view class="flex items-center justify-between py-20rpx">
+          <view class="text-28rpx text-#333">
+            配送费
+          </view>
+          <view class="text-28rpx text-#333">
+            ¥{{ totalProduct?.transfee }}
+          </view>
+        </view>
+      </view>
+      <view class="h200rpx" />
+    </Zpopup>
     <Zpopup v-model="cartPopup" :zindex="99">
-      <view class="ios h800rpx overflow-y-scroll">
-        <view class="p24rpx">
-          <view
-            v-for="item in cartList" :key="item.id" class="relative mt20rpx box-border flex items-center overflow-hidden rounded-16rpx bg-white p24rpx"
-          >
-            <view class="flex flex-1">
-              <image
-                :src="item.pic"
-                class="h206rpx w200rpx flex-shrink-0"
-                @click.stop="router.push({ name: 'xsb-goods', params: { id: String(item.prodId) } })"
-              />
-              <view class="ml20rpx flex-1">
-                <view class="text-left text-28rpx font-semibold">
-                  <!-- <view v-for="i in 2" :key="i" class="mr5px inline-block">
-                        <wd-tag type="danger">
-                          惊喜回馈
-                        </wd-tag>
-                      </view> -->
-                  {{ item.skuName }}
-                </view>
-                <view class="mt14rpx text-24rpx text-#AAAAAA">
-                  规格:{{ item.spec }}
-                </view>
-                <view class="mt14rpx flex items-center justify-between">
-                  <view class="text-36rpx text-#FF4A39 font-semibold">
-                    ¥{{ item.price }}
-                  </view>
-                  <!-- <wd-input-number v-model="item.num" disable-input @change="handleChangeNum($event, item)" /> -->
-                  <view v-if="item.shopSkuStocks !== '0' && item.isDelete !== '1'" class="flex items-center">
-                    <image
-                      :src="` ${StaticUrl}/sub-cart.png`"
-                      class="h44rpx w44rpx"
-                      @click.stop="handleSub(item)"
-                    />
-                    <view class="box-border h44rpx w84rpx flex items-center justify-center border border-#F0F0F0 border-solid text-24rpx text-#AAAAAA">
-                      {{ item.num }}
+      <view class="ios overflow-hidden">
+        <!-- 头部:全选 + 已选件数 + 清空购物车 -->
+        <view class="flex items-center justify-between border-b border-[#F5F5F5] border-solid px-24rpx py-24rpx">
+          <view class="flex items-center">
+            <wd-checkbox :model-value="isAllSelected" size="large" @change="toggleSelectAll">
+              全选
+            </wd-checkbox>
+            <text class="ml-16rpx text-24rpx text-[#AAAAAA]">
+              已选{{ selectedCount }}件,总重量约为{{ totalProduct?.totalWeight || 0 }}kg
+            </text>
+          </view>
+          <view class="text-24rpx text-[#AAAAAA]" @click="handleClearCart">
+            <wd-icon name="delete" size="24rpx" /> 清空购物车
+          </view>
+        </view>
+        <!-- 商品列表 -->
+        <view class="h-700rpx overflow-y-scroll">
+          <view class="p-24rpx">
+            <wd-checkbox-group v-model="selectedIds" custom-class="custom-checkbox-group" @change="getGoodsPrice">
+              <view
+                v-for="item in cartList" :key="item.id"
+                class="relative mt-20rpx box-border flex items-center overflow-hidden rounded-16rpx bg-white p-24rpx"
+              >
+                <!-- 复选框 -->
+                <wd-checkbox
+                  v-if="item.shopSkuStocks !== '0' && item.isDelete !== '1'" :model-value="item.id"
+                  size="large" class="mr-16rpx flex-shrink-0"
+                />
+                <view v-else class="mr-16rpx h-40rpx w-40rpx flex-shrink-0" />
+                <view class="flex flex-1">
+                  <image
+                    :src="item.pic" class="h206rpx w200rpx flex-shrink-0"
+                    @click.stop="router.push({ name: 'xsb-goods', params: { id: String(item.prodId) } })"
+                  />
+                  <view class="ml20rpx flex-1">
+                    <view class="text-left text-28rpx font-semibold">
+                      {{ item.skuName }}
+                    </view>
+                    <view class="mt14rpx text-24rpx text-#AAAAAA">
+                      规格:{{ item.spec }}
+                    </view>
+                    <view class="mt14rpx flex items-center justify-between">
+                      <view class="text-36rpx text-#FF4A39 font-semibold">
+                        ¥{{ item.price }}
+                      </view>
+                      <view v-if="item.shopSkuStocks !== '0' && item.isDelete !== '1'" class="flex items-center">
+                        <image
+                          :src="` ${StaticUrl}/sub-cart.png`" class="h44rpx w44rpx"
+                          @click.stop="handleSub(item)"
+                        />
+                        <view
+                          class="box-border h44rpx w84rpx flex items-center justify-center border border-#F0F0F0 border-solid text-24rpx text-#AAAAAA"
+                        >
+                          {{ item.num }}
+                        </view>
+                        <image
+                          :src="` ${StaticUrl}/add-cart.png`" class="h44rpx w44rpx"
+                          @click.stop="handleAdd(item)"
+                        />
+                      </view>
                     </view>
-                    <image
-                      :src="` ${StaticUrl}/add-cart.png`"
-                      class="h44rpx w44rpx"
-                      @click.stop="handleAdd(item)"
-                    />
                   </view>
                 </view>
-              </view>
-            </view>
-            <view v-if="item.shopSkuStocks == '0' || item.isDelete == '1'" class="absolute left-0 top-0 z-1 h-full w-full bg-[rgba(255,255,255,.6)]">
-              <view class="relative w-full flex items-center justify-center">
-                <view class="rounded-16rpx bg-[rgba(0,0,0,.5)] p20rpx text-white">
-                  {{ item.shopSkuStocks == '0' ? '商品已售罄' : '商品已删除' }}
-                </view>
-                <view class="absolute bottom-20rpx right-20rpx">
-                  <wd-button @click="handleDelCartGoods(item)">
-                    删除商品
-                  </wd-button>
+                <view
+                  v-if="item.shopSkuStocks == '0' || item.isDelete == '1'"
+                  class="absolute left-0 top-0 z-1 h-full w-full bg-[rgba(255,255,255,.6)]"
+                >
+                  <view class="relative w-full flex items-center justify-center">
+                    <view class="rounded-16rpx bg-[rgba(0,0,0,.5)] p20rpx text-white">
+                      {{ item.shopSkuStocks == '0' ? '商品已售罄' : '商品已删除' }}
+                    </view>
+                    <view class="absolute bottom-20rpx right-20rpx">
+                      <wd-button @click="handleDelCartGoods(item)">
+                        删除商品
+                      </wd-button>
+                    </view>
+                  </view>
                 </view>
               </view>
-            </view>
+            </wd-checkbox-group>
           </view>
+          <StatusTip v-if="!cartList.length" tip="暂无内容" />
+          <view class="h320rpx" />
         </view>
-        <StatusTip v-if="!cartList.length" tip="暂无内容" />
-
-        <view class="h320rpx" />
       </view>
     </Zpopup>
-    <selectSku v-model:show="selectGoods" v-model:sku-id="selectSkuId" v-model:select-goods-num="SelectGoodsNum" :goods-info="goodsInfo!">
+    <selectSku
+      v-model:show="selectGoods" v-model:sku-id="selectSkuId" v-model:select-goods-num="SelectGoodsNum"
+      :goods-info="goodsInfo!"
+    >
       <template #footer>
         <view class="box-border w-full flex items-center justify-between py20rpx">
           <view class="w-48%">
-            <wd-button hairline plain block @click="selectGoods = false">
+            <wd-button plain hairline block @click="selectGoods = false">
               取消
             </wd-button>
           </view>
@@ -679,8 +875,9 @@ function handlePay() {
   background: linear-gradient(180deg, #FAFFEC 0%, #F6FFDE 11%, #E7FFA5 100%);
   box-shadow: -10rpx 0rpx 12rpx 2rpx rgba(0, 0, 0, 0.07);
 }
-.fixedShadow{
-    box-shadow: 0rpx -6rpx 12rpx 2rpx rgba(0,0,0,0.05);
+
+.fixedShadow {
+  box-shadow: 0rpx -6rpx 12rpx 2rpx rgba(0, 0, 0, 0.05);
 }
 
 .wraper {
@@ -717,6 +914,9 @@ function handlePay() {
 }
 
 .cart-img {
-  animation: moveY .5s cubic-bezier(1,-1.26,1,1) forwards;
+  animation: moveY .5s cubic-bezier(1, -1.26, 1, 1) forwards;
+}
+:deep(.wd-checkbox-group){
+  background-color: transparent !important;
 }
 </style>

+ 96 - 0
src/subPack-xsb/commonTab/components/couponArrivalPopup.vue

@@ -0,0 +1,96 @@
+<script setup lang="ts">
+import { dayjs } from 'wot-design-uni'
+import { StaticUrl } from '@/config'
+
+const emit = defineEmits<{
+  use: [coupon: Api.AppMemberCouponVO]
+}>()
+const show = ref(false)
+
+const couponList = ref<Api.AppMemberCouponVO[]>([])
+
+function getCouponTitle(coupon: Api.AppMemberCouponVO) {
+  return coupon.activityName || coupon.couponName || '星闪豹优惠券'
+}
+
+function getCouponThreshold(coupon: Api.AppMemberCouponVO) {
+  return Number(coupon.amountMoney || 0) > 0 ? `满${coupon.amountMoney}可用` : '无门槛'
+}
+
+function getCouponExpireTime(coupon: Api.AppMemberCouponVO) {
+  if (!coupon.expirationTime) {
+    return '长期有效'
+  }
+  return dayjs(coupon.expirationTime).format('YYYY-MM-DD HH:mm:ss')
+}
+
+function handleUse(coupon: Api.AppMemberCouponVO) {
+  show.value = false
+  emit('use', coupon)
+}
+async function tryShowCouponArrivalPopup() {
+  try {
+    const res = await Apis.xsb.newCouponPopup({})
+    couponList.value = res.data ?? []
+    show.value = couponList.value.length > 0
+  }
+  catch (error) {
+    console.error('get coupon arrival popup failed', error)
+  }
+}
+tryShowCouponArrivalPopup()
+</script>
+
+<template>
+  <wd-overlay :show="show" :z-index="100001">
+    <view class="h-full w-full flex items-center justify-center">
+      <view :style="{ backgroundImage: `url(${StaticUrl}/xsb-coupon-idx-popup-bg.png)` }" class="relative h1078rpx w702rpx bg-contain bg-no-repeat" @click.stop>
+        <view class="absolute left-0 top-180rpx box-border h-full w-full flex justify-center px-66rpx pb-60rpx pt-184rpx">
+          <scroll-view scroll-y class="mt-110rpx box-border h-362rpx w500rpx flex justify-center px-20rpx pb-20rpx">
+            <view v-for="coupon in couponList" :key="coupon.id || coupon.allowanceId" :style="{ backgroundImage: `url(${StaticUrl}/xsb-coupon-idx-bg-popup.png)` }" class="relative mb-20rpx h-164rpx w450rpx bg-contain bg-no-repeat">
+              <view class="absolute left-0 top-0 box-border h-full w-full flex items-center px12rpx py24rpx">
+                <view class="h-full w-full flex items-center">
+                  <view class="box-border w115rpx flex-shrink-0 text-center">
+                    <view class="absolute left-0 top-0 inline-flex rounded-br-10rpx rounded-tl-10rpx bg-[#FF5A36] px-16rpx py-6rpx text-20rpx text-white">
+                      {{ Number(coupon.amountMoney) > 0 ? '满减券' : '无门槛' }}
+                    </view>
+                    <view class="mt-18rpx text-[#FF5331] font-bold">
+                      <text class="text-20rpx">
+                        ¥
+                      </text>
+                      <text class="text-40rpx leading-none">
+                        {{ coupon.discountMoney ?? 0 }}
+                      </text>
+                    </view>
+                    <view class="mt-10rpx text-24rpx text-[#666666]">
+                      {{ getCouponThreshold(coupon) }}
+                    </view>
+                  </view>
+                  <view class="box-border min-w-0 flex-1 pl30rpx">
+                    <view class="text-28rpx text-[#333333] font-semibold">
+                      {{ getCouponTitle(coupon) }}
+                    </view>
+                    <view class="mt-20rpx text-24rpx text-[#9A9A9A]">
+                      有效期:
+                    </view>
+                    <view class="mt-6rpx text-24rpx text-[#A8A8A8]">
+                      {{ getCouponExpireTime(coupon) }}
+                    </view>
+                    <view class="coupon-arrival-card__action absolute right-22rpx top-50% rounded-full bg-[#FF5736] px-24rpx py-12rpx text-24rpx text-white -translate-y-50%" @click.stop="handleUse(coupon)">
+                      去使用
+                    </view>
+                  </view>
+                </view>
+              </view>
+            </view>
+          </scroll-view>
+        </view>
+        <image :src="`${StaticUrl}/xsb-coupon-idx-kaixing-btn.png`" class="absolute bottom-90rpx left-50% h112rpx w384rpx transform -translate-x-50%" @click.stop="show = false" />
+      </view>
+    </view>
+  </wd-overlay>
+</template>
+
+<style scoped lang="scss">
+
+</style>

+ 3 - 3
src/subPack-xsb/commonTab/components/index.vue

@@ -227,8 +227,8 @@ function handleChangeSwiper(e: UniHelper.SwiperOnChangeEvent) {
           </wd-notice-bar>
         </view>
         <view class="relative mt20rpx box-border" @click="useGlobalToast().show('敬请期待 !')">
-          <image :src="`${StaticUrl}/xsb-index.png`" class="h236rpx w-full" />
-          <view class="absolute left-0 top-0 box-border h-full w-full flex items-end justify-between px16rpx pb16rpx">
+          <image :src="`${StaticUrl}/xsb-index-bg.jpg`" class="h236rpx w-full rounded-16rpx" />
+          <!-- <view class="absolute left-0 top-0 box-border h-full w-full flex items-end justify-between px16rpx pb16rpx">
             <image
               :src="`${StaticUrl}/xsb-index1.png`"
               class="h148rpx w328rpx"
@@ -237,7 +237,7 @@ function handleChangeSwiper(e: UniHelper.SwiperOnChangeEvent) {
               :src="`${StaticUrl}/xsb-index2.png`"
               class="h148rpx w328rpx"
             />
-          </view>
+          </view> -->
         </view>
         <view class="my20rpx">
           <scroll-view scroll-x class="whitespace-nowrap">

+ 12 - 7
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>
 
@@ -116,10 +116,15 @@ function handleGo(item: { name: string }) {
               <image :src="`${StaticUrl}/7.png`" class="h50rpx w50rpx" />
             </template>
           </wd-cell>
-          <wd-cell title="评价" custom-title-class="cell-title" clickable is-link>
+          <!-- <wd-cell title="评价" custom-title-class="cell-title" clickable is-link>
             <template #icon>
               <image :src="`${StaticUrl}/8.png`" class="h50rpx w50rpx" />
             </template>
+          </wd-cell> -->
+          <wd-cell title="优惠券" custom-title-class="cell-title" clickable is-link @click="router.push({ name: 'xsb-coupon' })">
+            <template #icon>
+              <image :src="`${StaticUrl}/xsb-my-coupon.png`" class="h50rpx w50rpx" />
+            </template>
           </wd-cell>
         </wd-cell-group>
       </wd-card>

+ 26 - 2
src/subPack-xsb/commonTab/index.vue

@@ -1,4 +1,5 @@
 <script setup lang="ts">
+import CouponArrivalPopup from './components/couponArrivalPopup.vue'
 import indexHome from './components/index.vue'
 import cart from './components/cart.vue'
 import classfiy from './components/classfiy.vue'
@@ -22,7 +23,15 @@ definePage({
     disableScroll: true,
   },
 })
-const { opcity, backTop, SelectShopInfo, allShopHasPermission, xsbShopList } = storeToRefs(useSysXsbStore())
+const sysXsbStore = useSysXsbStore()
+const {
+  opcity,
+  backTop,
+  SelectShopInfo,
+  allShopHasPermission,
+  xsbShopList,
+  isClassfiyPageOpen,
+} = storeToRefs(sysXsbStore)
 const { userInfo } = storeToRefs(useUserStore())
 const commonCategoryData = ref<Api.xsbCategories[]>([])
 const { data: goodsList, isLastPage, page, error, reload, refresh } = usePagination((pageNum, pageSize) =>
@@ -49,6 +58,12 @@ function handleTabbarChange({ value }: { value: string }) {
   setTabbarItemActive(value)
   tabbarName.value = value
 }
+watch(() => isClassfiyPageOpen.value, () => {
+  if (isClassfiyPageOpen.value) {
+    handleChange('xsb-classfiy')
+    isClassfiyPageOpen.value = false
+  }
+})
 function setTabbarItemActive(name: string) {
   tabbarItems.value.forEach((item) => {
     if (item.name === name) {
@@ -82,6 +97,10 @@ async function getSearchData(type: number) {
   return data
 }
 
+function handleCouponArrivalUse() {
+  handleChange('xsb-classfiy')
+}
+
 onMounted(async () => {
   allShopHasPermission.value = false
   await useSysXsbStore().getAllShopList()
@@ -93,7 +112,9 @@ onMounted(async () => {
   loading.value = false
   queryPopupConfig()
 })
-onShow(() => refresh())
+onShow(() => {
+  refresh()
+})
 
 watch(() => SelectShopInfo.value.shopId, () => {
   getCategories()
@@ -213,6 +234,9 @@ async function queryPopupConfig() {
         <image class="h-784rpx w-750rpx" :src="curtainInfo.imageUrl" />
       </view>
     </wd-overlay>
+    <CouponArrivalPopup
+      @use="handleCouponArrivalUse"
+    />
   </view>
 </template>
 

+ 99 - 0
src/subPack-xsb/components/coupon/index.vue

@@ -0,0 +1,99 @@
+<script setup lang="ts">
+import { dayjs } from 'wot-design-uni'
+import { StaticUrl } from '@/config'
+import router from '@/router'
+
+defineProps<{
+  itemcoupon: Api.AppMemberCouponVO
+  /** 是否显示"去使用"按鈕,默认 true */
+  showUseBtn?: boolean
+  /** 隐藏所有操作按鈕(选券模式用) */
+  hideAllBtn?: boolean
+}>()
+const {
+  isClassfiyPageOpen,
+} = storeToRefs(useSysXsbStore())
+function handleUseCoupon() {
+  isClassfiyPageOpen.value = true
+  // router.replace({ name: 'xsb-homeTabbar', params: { name: 'xsb-classfiy' } })
+  router.back()
+}
+</script>
+
+<template>
+  <view class="relative">
+    <view class="relative box-border h164rpx w-full flex overflow-hidden rounded-16rpx">
+      <image
+        v-if="itemcoupon.useStatus !== 2 || itemcoupon.isUsed === 1"
+        :src="`${StaticUrl}/xsb-coupon-bg-un.png`"
+        class="h-full w-full"
+      />
+      <image
+        v-else
+        :src="`${StaticUrl}/xsb-coupon-bg-item.png`"
+        class="h-full w-full"
+      />
+      <view class="absolute left-0 top-0 h-full w-full flex items-center">
+        <view class="w-184rpx flex flex-shrink-0 flex-col items-center justify-center py-36rpx">
+          <view class="flex items-end leading-none" :class="[itemcoupon.useStatus == 2 || itemcoupon.isUsed == 2 ? 'text-[#FF4A39]' : 'text-[#AAAAAA]']">
+            <text class="mb-6rpx text-24rpx font-semibold">
+              ¥
+            </text>
+            <text class="text-48rpx font-bold">
+              {{ itemcoupon.discountMoney ?? 0 }}
+            </text>
+          </view>
+          <view class="mt-12rpx text-24rpx text-[#AAAAAA]">
+            {{ Number(itemcoupon.amountMoney) > 0 ? `满${itemcoupon.amountMoney}可用` : '无门槛' }}
+          </view>
+        </view>
+        <view class="flex flex-1 items-center py-28rpx pr-24rpx">
+          <view class="flex-1 pr-20rpx">
+            <view class="text-28rpx text-[#333] font-semibold">
+              {{ itemcoupon.activityName || itemcoupon.couponName || '优惠券' }}
+            </view>
+            <template v-if="[2, 5].includes(itemcoupon.useStatus || 2)">
+              <view v-if="itemcoupon.expirationTime" class="mt-12rpx text-22rpx text-[#AAAAAA]">
+                有效期:{{ dayjs(itemcoupon.expirationTime).format('YYYY-MM-DD HH:mm:ss') }}
+              </view>
+              <view v-if="itemcoupon.scopeDesc" class="mt-8rpx text-22rpx text-[#AAAAAA]">
+                限:{{ itemcoupon.scopeDesc }}
+              </view>
+            </template>
+            <view v-if="itemcoupon.orderCreateTime" class="mt-8rpx text-22rpx text-[#AAAAAA]">
+              使用时间: {{ itemcoupon.orderCreateTime }}
+            </view>
+            <view v-if="itemcoupon.lockOrderId" class="mt-8rpx flex items-center text-22rpx text-[#AAAAAA]">
+              订单号:{{ itemcoupon.lockOrderId }}
+            </view>
+            <view v-if="itemcoupon.useStatus === 1 && itemcoupon.isRnum == 1" class="mt-8rpx text-22rpx text-[#AAAAAA]">
+              说明:部分退款,优惠券不退还
+            </view>
+            <view v-if="itemcoupon.useStatus === 7" class="mt-8rpx text-22rpx text-[#AAAAAA]">
+              冻结原因:该券已被占用,取消可恢复,有效期内可用。
+            </view>
+          </view>
+          <template v-if="!hideAllBtn">
+            <view
+              v-if="showUseBtn !== false"
+              class="flex-shrink-0 rounded-full bg-[#FF4A39] px-28rpx py-16rpx text-26rpx text-white"
+              @click="handleUseCoupon"
+            >
+              去使用
+            </view>
+            <wd-button v-if="itemcoupon.useStatus === 1" hairline plain size="small" type="info" @click="router.replace({ name: 'xsb-orderDetaile', params: { id: String(itemcoupon.lockOrderId) } })">
+              查看订单
+            </wd-button>
+            <wd-button v-if="itemcoupon.useStatus === 7" plain hairline size="small" type="info" @click="router.replace({ name: 'xsb-orderDetaile', params: { id: String(itemcoupon.lockOrderId) } })">
+              去取消订单
+            </wd-button>
+          </template>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+
+</style>

+ 188 - 23
src/subPack-xsb/confirmOrder/index.vue

@@ -1,4 +1,5 @@
 <script setup lang="ts">
+import CouponItem from '../components/coupon/index.vue'
 import router from '@/router'
 
 definePage({
@@ -14,18 +15,63 @@ const orderInfo = ref<Api.shoppingCartOrderConfirm>()
 const { totalProduct } = storeToRefs(useSmqjhCartStore())
 const isPay = ref(false)
 const remarks = ref('')
+const initialCouponId = ref<string | undefined>(undefined)
+const confirmedCouponId = ref<string | undefined>(undefined)
+const draftCouponId = ref<string | undefined>(undefined)
 
+const deliveryType = ref(1)
+
+// 优惠券选择
+const couponPopup = ref(false)
+const couponList = ref<Api.AppMemberCouponVO[]>([])
+const couponTabActive = ref(0)
+const availableCoupons = computed(() => couponList.value.filter(it => it.isUsed === 2))
+const unavailableCoupons = computed(() => couponList.value.filter(it => it.isUsed !== 2))
+const displayCoupons = computed(() => couponTabActive.value === 0 ? availableCoupons.value : unavailableCoupons.value)
+const confirmedCoupon = computed(() => couponList.value.find(it => it.allowanceId === confirmedCouponId.value))
+const draftCoupon = computed(() => couponList.value.find(it => it.allowanceId === draftCouponId.value))
+const hasManualCouponSelection = computed(() => confirmedCouponId.value !== initialCouponId.value)
+const currentCouponDiscount = computed(() => {
+  if (hasManualCouponSelection.value) {
+    return Number(confirmedCoupon.value?.discountMoney || 0)
+  }
+  return Number(orderInfo.value?.coupon || 0)
+})
 const offsetPoints = computed(() => {
-  const money = (Number(unref(orderInfo)?.transfee) * 100) + (Number(unref(orderInfo)?.price) * 100)
-  if (Number(unref(orderInfo)?.offsetPoints) > money) {
-    return money
+  const couponCents = Math.round(currentCouponDiscount.value * 100)
+  const money = Math.round(Number(unref(orderInfo)?.transfee) * 100) + Math.round(Number(unref(orderInfo)?.price) * 100) - couponCents
+  const cap = Math.max(0, money)
+  if (Number(unref(orderInfo)?.offsetPoints) > cap) {
+    return cap
   }
   return Number(unref(orderInfo)?.offsetPoints)
 })
-const deliveryType = ref(1)
+const currentCouponName = computed(() => {
+  if (hasManualCouponSelection.value) {
+    return getCouponDisplayName(confirmedCoupon.value)
+  }
+  return getCouponDisplayName(orderInfo.value)
+})
+const displayTotalPrice = computed(() => {
+  if (!orderInfo.value) {
+    return 0
+  }
+  if (!hasManualCouponSelection.value) {
+    return Number(orderInfo.value.totalPrice || 0)
+  }
+  const goodsPrice = Number(orderInfo.value.price || 0)
+  const freight = Number(orderInfo.value.transfee || 0)
+  const pointsDiscount = offsetPoints.value / 100
+  return Math.max(0, Number((goodsPrice + freight - currentCouponDiscount.value - pointsDiscount).toFixed(2)))
+})
+const displayDiscountTotal = computed(() => Number((currentCouponDiscount.value + offsetPoints.value / 100).toFixed(2)))
+
 onLoad((options: any) => {
-  console.log(options.data)
   orderInfo.value = JSON.parse(options.data)
+  couponList.value = orderInfo.value?.orderCouponItemDTOS || []
+  initialCouponId.value = orderInfo.value?.allowanceId
+  confirmedCouponId.value = initialCouponId.value
+  draftCouponId.value = initialCouponId.value
 })
 onShow(() => {
   useUserStore().getuserAddresslist()
@@ -34,11 +80,15 @@ onShow(() => {
   }
 })
 
+function getCouponDisplayName(coupon?: Partial<Api.AppMemberCouponVO> & Record<string, any>) {
+  return coupon?.activityName || coupon?.couponName || ''
+}
+
 async function getDevryList() {
   const res = await Apis.xsb.delivery({
     data: {
       memberId: userInfo.value.id,
-      shopId: SelectShopInfo.value.shopId,
+      shopId: Number(orderInfo.value?.skuList[0].shopId || SelectShopInfo.value?.shopId || 2),
       addressId: selectedAddress.value?.id,
     },
   })
@@ -46,6 +96,23 @@ async function getDevryList() {
   console.log(res)
 }
 
+async function openCouponPopup() {
+  draftCouponId.value = confirmedCouponId.value
+  couponPopup.value = true
+}
+
+function handleSelectCoupon(item: Api.AppMemberCouponVO) {
+  if (item.isUsed !== 2 || !item.allowanceId) {
+    return
+  }
+  draftCouponId.value = item.allowanceId
+}
+
+function confirmCoupon() {
+  confirmedCouponId.value = draftCouponId.value
+  couponPopup.value = false
+}
+
 async function handlePay() {
   if (!selectedAddress.value) {
     useGlobalToast().show({ msg: '请选择收货地址' })
@@ -64,11 +131,18 @@ async function handlePay() {
       }
     })
 
-    const orderNumber = await useUserStore().getOrderPayMent(orderInfo.value.transfee, 'XSB', deliveryType.value, Number(orderInfo.value?.skuList[0].shopId || SelectShopInfo.value.shopId), orderItemList, unref(remarks))
+    const orderNumber = await useUserStore().getOrderPayMent(
+      orderInfo.value.transfee,
+      'XSB',
+      deliveryType.value,
+      Number(orderInfo.value?.skuList[0].shopId || SelectShopInfo.value?.shopId),
+      orderItemList,
+      unref(remarks),
+      confirmedCouponId.value,
+    )
     const res = await useUserStore().handleCommonPayMent(orderNumber)
     await useUserStore().clearCart(orderInfo.value.skuList)
     totalProduct.value = null
-    console.log('进入微信支付', res)
     if (res.payType !== 1) {
       try {
         await useUserStore().getWxCommonPayment(res)
@@ -90,7 +164,10 @@ async function handlePay() {
 
 <template>
   <view class="page px20rpx py20rpx">
-    <view class="mb20rpx rounded-16rpx bg-white p24rpx" @click="router.push({ name: 'common-addressList', params: { type: 'select' } })">
+    <view
+      class="mb20rpx rounded-16rpx bg-white p24rpx"
+      @click="router.push({ name: 'common-addressList', params: { type: 'select' } })"
+    >
       <view class="flex items-center justify-between">
         <view v-if="!selectedAddress" class="flex items-center">
           <wd-icon name="location" size="18px" />
@@ -128,10 +205,7 @@ async function handlePay() {
       <CollapsePanel :line-height="150">
         <view v-for="item in orderInfo?.skuList" :key="item.id" class="mb20rpx w-full flex items-center">
           <view class="mr20rpx w120rpx flex-shrink-0">
-            <image
-              :src="item.pic"
-              class="h120rpx w120rpx"
-            />
+            <image :src="item.pic" class="h120rpx w120rpx" />
           </view>
           <view class="flex-1">
             <view class="w-full flex items-center justify-between font-semibold">
@@ -170,6 +244,18 @@ async function handlePay() {
           ¥{{ orderInfo?.transfee }}
         </view>
       </view>
+      <view class="mb28rpx flex items-center justify-between text-28rpx" @click="openCouponPopup">
+        <view>优惠券</view>
+        <view class="flex items-center">
+          <view v-if="currentCouponDiscount > 0" class="text-[#FF4D3A] font-semibold">
+            -¥{{ currentCouponDiscount }}
+          </view>
+          <view v-else class="text-[#AAAAAA]">
+            {{ availableCoupons.length > 0 ? `${availableCoupons.length}张可用` : '暂无可用' }}
+          </view>
+          <wd-icon name="arrow-right" size="18px" color="#aaa" class="ml10rpx" />
+        </view>
+      </view>
       <view class="flex items-center justify-between text-28rpx">
         <view>积分({{ offsetPoints }})</view>
         <view class="text-#FF4D3A font-semibold">
@@ -182,7 +268,7 @@ async function handlePay() {
           总计:
         </view>
         <view class="text-#FF4D3A font-semibold">
-          ¥ {{ orderInfo?.totalPrice }}
+          ¥ {{ displayTotalPrice }}
         </view>
       </view>
     </view>
@@ -195,19 +281,94 @@ async function handlePay() {
       </view>
     </view>
     <view class="h250rpx" />
+
+    <!-- 优惠券选择弹窗 -->
+    <Zpopup v-model="couponPopup" :zindex="100" bg="#fff">
+      <view class="ios box-border w-full">
+        <view class="px-32rpx pt-32rpx">
+          <view class="mb-24rpx text-center text-32rpx font-semibold">
+            优惠券
+          </view>
+          <view class="mb-24rpx flex items-center">
+            <view
+              class="mr-16rpx flex-1 rounded-full py-18rpx text-center text-28rpx"
+              :class="couponTabActive === 0 ? 'bg-[#9ED605] text-white font-semibold' : 'bg-[#F0F0F0] text-[#666]'"
+              @click="couponTabActive = 0"
+            >
+              可用券({{ availableCoupons.length }})
+            </view>
+            <view
+              class="flex-1 rounded-full py-18rpx text-center text-28rpx"
+              :class="couponTabActive === 1 ? 'bg-[#9ED605] text-white font-semibold' : 'bg-[#F0F0F0] text-[#666]'"
+              @click="couponTabActive = 1"
+            >
+              不可用券({{ unavailableCoupons.length }})
+            </view>
+          </view>
+          <view class="mb-20rpx flex items-center justify-between text-24rpx">
+            <view class="text-[#333]">
+              优惠券共减
+            </view>
+            <view v-if="draftCoupon" class="text-right text-[#FF4D3A]">
+              {{ getCouponDisplayName(draftCoupon) }},可减-¥{{ draftCoupon.discountMoney }}
+            </view>
+            <view v-else-if="currentCouponDiscount > 0 && currentCouponName" class="text-right text-[#FF4D3A]">
+              {{ currentCouponName }},可减¥{{ currentCouponDiscount }}
+            </view>
+            <view v-else class="text-[#AAAAAA]">
+              暂未选择
+            </view>
+          </view>
+        </view>
+        <scroll-view scroll-y class="box-border h-560rpx px-32rpx">
+          <view v-for="item in displayCoupons" :key="item.id" class="relative mb-20rpx">
+            <view class="relative" @click="handleSelectCoupon(item)">
+              <CouponItem :itemcoupon="item" :hide-all-btn="true" />
+              <view
+                class="absolute right-32rpx top-50% h-44rpx w-44rpx flex items-center justify-center -translate-y-50%"
+              >
+                <wd-icon v-if="draftCouponId === item.allowanceId" name="check-circle" size="44rpx" color="#FF4D3A" />
+                <view v-else class="h-40rpx w-40rpx border-2rpx border-[#ccc] rounded-full border-solid" />
+              </view>
+            </view>
+            <view
+              v-if="item.unavailableReason"
+              class="relative z-1 w-full rounded-b-16rpx bg-[#FFF4F3] px20rpx py18rpx text-24rpx -mt16rpx"
+            >
+              <view>
+                不可用原因
+              </view>
+              <view>
+                {{ item.unavailableReason }}
+              </view>
+            </view>
+          </view>
+          <view v-if="displayCoupons.length === 0" class="py-80rpx text-center text-28rpx text-[#AAAAAA]">
+            暂无优惠券
+          </view>
+        </scroll-view>
+      </view>
+      <template #footer>
+        <view class="box-border w-full px24rpx">
+          <wd-button size="large" block type="primary" @click="confirmCoupon">
+            确定
+          </wd-button>
+        </view>
+      </template>
+    </Zpopup>
     <view class="ios footer fixed bottom-0 left-0 box-border w-full rounded-t-16rpx bg-white px24rpx">
       <view class="box-border w-full flex items-center justify-between py20rpx">
         <view class="flex items-center text-#FF4D3A">
           <view class="font-semibold10 flex items-baseline text-36rpx">
             <text class="text-24rpx">
-            </text> {{ orderInfo?.totalPrice }}
+            </text> {{ displayTotalPrice }}
           </view>
           <view class="ml20rpx text-22rpx">
-            共减¥{{ offsetPoints / 100 }}
+            共减¥{{ displayDiscountTotal }}
           </view>
         </view>
-        <view class="w180rpx">
+        <view class="w180-btn w180rpx">
           <wd-button size="large" :loading="isPay" loading-color="#9ED605" @click="handlePay">
             立即支付
           </wd-button>
@@ -218,14 +379,18 @@ async function handlePay() {
 </template>
 
 <style scoped lang="scss">
-  .footer{
-    box-shadow: 0rpx -6rpx 12rpx 2rpx rgba(0,0,0,0.05);
-  }
-  .page{
-    :deep(){
-      .wd-button{
+.footer {
+  box-shadow: 0rpx -6rpx 12rpx 2rpx rgba(0, 0, 0, 0.05);
+}
+
+.page {
+  .w180-btn {
+    :deep() {
+      .wd-button {
         width: 180rpx !important;
       }
     }
   }
+
+}
 </style>

+ 84 - 0
src/subPack-xsb/coupon/index.vue

@@ -0,0 +1,84 @@
+<script setup lang="ts">
+import CouponItem from '../components/coupon/index.vue'
+
+definePage({ name: 'xsb-coupon', islogin: true, style: { navigationBarTitleText: '优惠券列表' } })
+
+const tabList = [
+  { label: '待使用', useStatus: 2 },
+  { label: '已使用', useStatus: 1 },
+  { label: '已过期', useStatus: 5 },
+  { label: '冻结', useStatus: 7 },
+]
+
+const activeTab = ref(0)
+const activeCoupon = ref(null)
+
+const { data: couponList, isLastPage, page, reload } = usePagination(
+  (pageNum, pageSize) => Apis.xsb.memberCouponPage({
+    data: {
+      pageNum,
+      pageSize,
+      useStatus: tabList[activeTab.value].useStatus,
+    },
+  }),
+  {
+    immediate: true,
+    pageNum: 1,
+    pageSize: 10,
+    initialData: [],
+    data: res => res.data?.list ?? [],
+    append: true,
+  },
+)
+onLoad((options: any) => {
+  console.log(options)
+  if (options.couponId) {
+    activeCoupon.value = options.couponId
+    switchTab(Number(options.activeTab))
+  }
+})
+
+function switchTab(index: number) {
+  if (activeTab.value === index)
+    return
+  activeTab.value = index
+  couponList.value = []
+  reload()
+}
+
+onReachBottom(() => {
+  if (!isLastPage.value) {
+    page.value++
+  }
+})
+</script>
+
+<template>
+  <view class="min-h-screen bg-white">
+    <!-- Tab 导航 -->
+    <view class="sticky top-0 z-10 bg-white">
+      <wd-tabs v-model="activeTab" @change="switchTab($event.index)">
+        <wd-tab v-for="tab in tabList" :key="tab.useStatus" :title="tab.label" />
+      </wd-tabs>
+    </view>
+
+    <!-- 列表 -->
+    <view class="p-24rpx">
+      <view v-for="item in couponList" :key="item.id" class="mb-20rpx">
+        <view :class="[item.allowanceId == activeCoupon ? 'border-2rpx border-red-4 rounded-16rpx border-solid' : '']">
+          <CouponItem
+            :itemcoupon="item"
+            :show-use-btn="item.useStatus === 2"
+          />
+        </view>
+      </view>
+
+      <!-- 空状态 -->
+      <StatusTip v-if="couponList.length === 0" tip="暂无内容" />
+      <!-- 底部占位 -->
+      <view class="h-40rpx" />
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped></style>

+ 36 - 20
src/subPack-xsb/goods/index.vue

@@ -37,7 +37,16 @@ const isShowTab = ref(false)
 const goodsId = ref()
 onLoad((option: any) => {
   goodsId.value = option.id
-  getGoodsDetaile()
+  console.log(option, 'option====================')
+
+  if (option.q) {
+    const { id, storeId, channelId } = parseUrlParams(option.q)
+    goodsId.value = id
+    getGoodsDetaile(Number(storeId), Number(channelId))
+  }
+  else {
+    getGoodsDetaile(option.storeId, option.channelId)
+  }
 })
 
 const goodsInfo = ref<Api.xsbProductDetail>()
@@ -87,8 +96,8 @@ function handleGoCurren(id: number) {
   uni.pageScrollTo({ selector: `.view-${id}` })
 }
 
-async function getGoodsDetaile() {
-  const res = await Apis.xsb.getProductDetail({ data: { id: goodsId.value, shopId: Number(SelectShopInfo.value?.shopId) || 2, channelId: userInfo.value.channelId || 1 } })
+async function getGoodsDetaile(shopId: number, channelId: number) {
+  const res = await Apis.xsb.getProductDetail({ data: { id: goodsId.value, shopId: Number(SelectShopInfo.value?.shopId) || shopId || 2, channelId: userInfo.value.channelId || channelId || 1 } })
   console.log(res, '请求')
   if (!res.data) {
     useGlobalToast().show('暂无该商品查看权限!')
@@ -104,24 +113,28 @@ async function handleConfimOrder() {
   }
   await useUserStore().checkLogin()
   isLoging.value = true
-  const res = await Apis.xsb.skuOrderConfirm({
-    data: {
-      skuId: specId.value,
-      num: SelectGoodsNum.value,
-      channelId: unref(userInfo).channelId,
-      shopId: unref(goodsInfo)?.shopId || 2,
-    },
-  })
-  if (Number(res.data.sku?.shopSkuStocks) < unref(SelectGoodsNum)) {
-    useGlobalToast().show('库存不足,请调整购买数量')
+  try {
+    const res = await Apis.xsb.skuOrderConfirm({
+      data: {
+        skuId: specId.value,
+        num: SelectGoodsNum.value,
+        channelId: unref(userInfo).channelId,
+        shopId: unref(goodsInfo)?.shopId || 2,
+      },
+    })
+    if (Number(res.data.sku?.shopSkuStocks) < unref(SelectGoodsNum)) {
+      useGlobalToast().show('库存不足,请调整购买数量')
+      isLoging.value = false
+      return
+    }
+    router.push({
+      name: 'xsb-confirmOrder',
+      params: { data: JSON.stringify({ ...res.data, skuList: [{ ...res.data.sku, num: SelectGoodsNum.value, shopId: unref(goodsInfo)?.shopId || 2 }] }) },
+    })
+  }
+  finally {
     isLoging.value = false
-    return
   }
-  router.push({
-    name: 'xsb-confirmOrder',
-    params: { data: JSON.stringify({ ...res.data, skuList: [{ ...res.data.sku, num: SelectGoodsNum.value }] }) },
-  })
-  isLoging.value = false
 }
 async function handleAddCart() {
   if (Number(unref(goodsInfo)?.spuStock) < unref(SelectGoodsNum)) {
@@ -140,6 +153,9 @@ async function handleAddCart() {
     console.log(error)
   }
 }
+function handleClick() {
+  uni.previewImage({ urls: swiperList.value as string[] })
+}
 </script>
 
 <template>
@@ -150,7 +166,7 @@ async function handleAddCart() {
       safe-area-inset-top left-arrow fixed @click-left="router.back()"
     />
     <template v-if="goodsInfo">
-      <wd-swiper v-model:current="current" :list="swiperList" :autoplay="false" :height="375">
+      <wd-swiper v-model:current="current" :list="swiperList" :autoplay="false" :height="375" @click="handleClick">
         <template #indicator="{ current: currentIndex, total }">
           <view class="custom-indicator absolute bottom-60rpx right-20rpx px26rpx">
             {{ currentIndex + 1 }}/{{ total }}

+ 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>

+ 47 - 22
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) {
@@ -265,6 +250,9 @@ async function handleReceive() {
   await useUserStore().handleCommonOrderReceive(orderInfo.value as Api.xsbOrderList)
   getDetail(String(unref(orderInfo)?.orderNumber))
 }
+function handleRefundDetail(item: any) {
+  router.push({ name: 'common-afterSalesDetail', params: { refundNumber: String(item.refundNumber) } })
+}
 </script>
 
 <template>
@@ -388,7 +376,7 @@ async function handleReceive() {
             </Zcontact>
           </view>
           <view
-            v-if="orderInfo.refundStatus != 2 && [OrderStatus.OrderCompleted, OrderStatus.OrderWaitDelivery, OrderStatus.OrderAccepted].includes(orderInfo.hbOrderStatus)"
+            v-if="orderInfo.refundStatus != 2 && [OrderStatus.OrderWaitDelivery, OrderStatus.OrderAccepted].includes(orderInfo.hbOrderStatus)"
             class="flex flex-col items-center" @click="handleAfterSale"
           >
             <image :src="`${StaticUrl}/orderDetaile-shou.png`" class="h-40rpx w-40rpx" />
@@ -480,6 +468,23 @@ async function handleReceive() {
             ¥{{ orderInfo?.freightAmount }}
           </view>
         </view>
+        <view class="mt-24rpx flex items-center justify-between">
+          <view class="text-28rpx">
+            优惠券
+          </view>
+          <view class="text-[#FF4A39] font-semibold">
+            -¥{{ Number(orderInfo?.couponBaseInfoDTO?.discountMoney) || 0 }}
+          </view>
+        </view>
+        <view v-if="orderInfo.refundOrderList && orderInfo.refundOrderList.some((it) => it.refundOrderStatus == 1) " class="mt10rpx rounded-16rpx bg-#ccc p24rpx text-24rpx">
+          <view v-if="orderInfo?.isAll">
+            已退还优惠券:{{ orderInfo.couponBaseInfoDTO?.activityName }}
+            <view>优惠券已退回您的账户,可在“我的-优惠券”中查看</view>
+          </view>
+          <view v-else>
+            注:部分退款不退还优惠券
+          </view>
+        </view>
         <view class="mt-24rpx flex items-center justify-between">
           <view class="text-28rpx">
             积分
@@ -491,7 +496,7 @@ async function handleReceive() {
         <view class="my-24rpx h-2rpx w-full bg-[#F0F0F0]" />
         <view class="flex items-center justify-end">
           <view class="text-28rpx">
-            总计{{ orderInfo.orderMoney }} 共减 {{ Number(orderInfo.offsetPoints) / 100 }}
+            总计{{ orderInfo.orderMoney }} 共减 {{ Number(orderInfo.offsetPoints) / 100 + (Number(orderInfo.couponBaseInfoDTO?.discountMoney) || 0) }}
           </view>
           <view class="ml-20rpx text-28rpx text-[#FF4D3A] font-semibold">
             {{ [OrderStatus.PaddingPay, OrderStatus.OrderCancel].includes(Number(orderInfo.hbOrderStatus)) ? '需' : '实'
@@ -500,6 +505,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" @click="handleRefundDetail(item)">
+          <view class="flex items-center justify-between">
+            <view>{{ item.refundName }} </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">
           订单信息

+ 11 - 2
src/subPack-xsb/store-xsb/sys.ts

@@ -33,7 +33,14 @@ interface SysState {
    * 是否所有门店都有权限
    */
   allShopHasPermission: boolean
-
+  /**
+   * 优惠券到账弹窗是否显示
+   */
+  couponArrivalPopupVisible: boolean
+  /**
+   * 是否打开分类页面
+   */
+  isClassfiyPageOpen: boolean
 }
 export const useSysXsbStore = defineStore('system-xsb', {
   state: (): SysState => ({
@@ -46,6 +53,8 @@ export const useSysXsbStore = defineStore('system-xsb', {
     opcity: 0,
     selectAddressId: undefined,
     allShopHasPermission: false,
+    couponArrivalPopupVisible: false,
+    isClassfiyPageOpen: false,
   }),
   actions: {
     getTabbarItemValue(name: string) {
@@ -85,7 +94,7 @@ export const useSysXsbStore = defineStore('system-xsb', {
         Apis.xsb.nearestShop({ data: { longitude, latitude } }).then((res) => {
           const newShop = this.xsbShopList.find(it => it.shopId === res.data.nearestShopId)
           if (newShop) {
-            this.SelectShopInfo = newShop
+            this.SelectShopInfo = this.SelectShopInfo || newShop
           }
           resolve(res)
         }).catch((err) => { reject(err) })

+ 13 - 1
src/uni-pages.d.ts

@@ -7,6 +7,7 @@ interface NavigateToOptions {
   url: "/pages/login/index" |
        "/subPack-xsb/commonTab/index" |
        "/subPack-xsb/confirmOrder/index" |
+       "/subPack-xsb/coupon/index" |
        "/subPack-xsb/goods/index" |
        "/subPack-xsb/order/index" |
        "/subPack-xsb/orderDetaile/index" |
@@ -24,6 +25,7 @@ interface NavigateToOptions {
        "/subPack-common/revalue/index" |
        "/subPack-common/revalueSuccess/index" |
        "/subPack-common/user-center/index" |
+       "/subPack-smqjh/bannerDetaile/index" |
        "/subPack-smqjh/order/index" |
        "/subPack-film/choose-film/index" |
        "/subPack-film/choose-seat/index" |
@@ -35,14 +37,18 @@ interface NavigateToOptions {
        "/subPack-film/order-detail/index" |
        "/subPack-film/select-time/index" |
        "/subPack-film/submit-order/index" |
+       "/subPack-charge/chargeAddPlate/chargeAddPlate" |
+       "/subPack-charge/chargeBuyaTicketList/chargeBuyaTicketList" |
        "/subPack-charge/chargeDetail/chargeDetail" |
        "/subPack-charge/chargeing/chargeing" |
        "/subPack-charge/chargeMap/chargeMap" |
        "/subPack-charge/chargeOrderDetail/chargeOrderDetail" |
        "/subPack-charge/chargeOrderList/chargeOrderList" |
+       "/subPack-charge/chargePlateList/chargePlateList" |
        "/subPack-charge/chargeSearchList/chargeSearchList" |
        "/subPack-charge/chargeSiteDetail/chargeSiteDetail" |
        "/subPack-charge/chargeStart/chargeStart" |
+       "/subPack-charge/chargeVoucher/chargeVoucher" |
        "/subPack-charge/index/index" |
        "/subPack-videoRights/commonTab/index" |
        "/subPack-videoRights/videoRightsDetail/videoRightsDetail" |
@@ -56,7 +62,13 @@ interface NavigateToOptions {
        "/subPack-djk/shopinfo/index" |
        "/subPack-djk/welfare/index" |
        "/subPack-refueling/commonTab/index" |
-       "/subPack-refueling/webView/index";
+       "/subPack-refueling/orderDetaile/index" |
+       "/subPack-refueling/webView/index" |
+       "/subPack-attractions/attractionsDetail/attractionsDetail" |
+       "/subPack-attractions/attractionsOrderDetail/attractionsOrderDetail" |
+       "/subPack-attractions/attractionsOrderPay/attractionsOrderPay" |
+       "/subPack-attractions/attractionsReservation/attractionsReservation" |
+       "/subPack-attractions/commonTab/index";
 }
 interface RedirectToOptions extends NavigateToOptions {}
 

+ 100 - 0
src/utils/index.ts

@@ -157,3 +157,103 @@ export class InputFormatUtil {
 export function phoneFormat(phone: string) {
   return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
 }
+
+/**
+ * 从 URL 中解析查询参数
+ * 支持已编码和未编码的 URL
+ * @param url URL 字符串,可以是完整 URL 或仅查询字符串部分
+ * @returns 解析后的参数对象
+ * @example
+ */
+export function parseUrlParams(url: string): Record<string, string> {
+  if (!url)
+    return {}
+
+  try {
+    // 尝试解码(处理多次编码的情况)
+    let decodedUrl = url
+    while (decodedUrl.includes('%')) {
+      const newDecoded = decodeURIComponent(decodedUrl)
+      if (newDecoded === decodedUrl)
+        break
+      decodedUrl = newDecoded
+    }
+
+    // 提取查询字符串部分
+    let queryString = decodedUrl
+    const questionIndex = decodedUrl.indexOf('?')
+    if (questionIndex !== -1) {
+      queryString = decodedUrl.substring(questionIndex + 1)
+    }
+
+    // 移除可能存在的 hash 部分
+    const hashIndex = queryString.indexOf('#')
+    if (hashIndex !== -1) {
+      queryString = queryString.substring(0, hashIndex)
+    }
+
+    if (!queryString)
+      return {}
+
+    // 解析参数
+    const params: Record<string, string> = {}
+    const pairs = queryString.split('&')
+
+    pairs.forEach((pair) => {
+      const equalIndex = pair.indexOf('=')
+      if (equalIndex > 0) {
+        const key = decodeURIComponent(pair.substring(0, equalIndex))
+        const val = decodeURIComponent(pair.substring(equalIndex + 1))
+        params[key] = val
+      }
+      else if (pair) {
+        params[decodeURIComponent(pair)] = ''
+      }
+    })
+
+    return params
+  }
+  catch {
+    return {}
+  }
+}
+
+/**
+ *处理富文本图片溢出
+ * @param html - 富文本字符串
+ * @returns
+ */
+export function fixImgStyle(html: string) {
+  if (!html)
+    return html
+  // 处理已有 style 属性的 img 标签:将样式追加到现有 style 中
+  let result = html.replace(/<img([^>]*)style\s*=\s*["']([^"']*)["']([^>]*)>/gi, (match, before, existingStyle, after) => {
+    // 移除可能存在的 width/height 内联样式,然后添加防溢出样式
+    const cleanedStyle = existingStyle.replace(/\bwidth\s*:[^;]*;?/gi, '').replace(/\bheight\s*:[^;]*;?/gi, '')
+    const newStyle = `${cleanedStyle};max-width:100%;height:auto;display:block;`.replace(/^;+|;+$/g, '').replace(/;{2,}/g, ';')
+    return `<img${before}style="${newStyle}"${after}>`
+  })
+
+  // 处理没有 style 属性的 img 标签:添加新的 style
+  result = result.replace(/<img(?![^>]*style\s*=)([^>]*)>/gi, '<img style="max-width:100%;height:auto;display:block;"$1>')
+
+  return result
+}
+
+// 从地址字符串中提取城市名称
+export function getCityName(address: string): string {
+  if (!address)
+    return ''
+
+  // 安全正则,无 ESLint 警告
+  const regex = /^[^省]+省([^市]+)/
+  const match = address.match(regex)
+
+  if (match) {
+    return match[1]
+  }
+
+  // 兼容直辖市(北京 / 上海 / 重庆 / 天津)
+  const municipality = address.match(/(北京|上海|重庆|天津)/)
+  return municipality ? municipality[1] : ''
+}

+ 1 - 0
vite.config.ts

@@ -34,6 +34,7 @@ export default async () => {
           'subPack-videoRights',
           'subPack-djk',
           'subPack-refueling',
+          'subPack-attractions',
         ],
         /**
          * 排除的页面,相对于 dir 和 subPackages