1
0

18 Revīzijas f0f4676ef2 ... 5d7236dcd9

Autors SHA1 Ziņojums Datums
  zouzexu 5d7236dcd9 feat(vip): 添加会员中心及相关功能页面 1 mēnesi atpakaļ
  wenjie 2c37390ebc ``` 1 mēnesi atpakaļ
  zhangtao ffcf0eaa8f fix(config): 修正 trial 环境地址配置 1 mēnesi atpakaļ
  zhangtao a101cc8f17 fix(config): 调整环境地址配置,修复优惠券退款逻辑 1 mēnesi atpakaļ
  zhangtao 6c2460bef9 feat(cart): 优化购物车功能与界面细节 1 mēnesi atpakaļ
  zhangtao 1a306419f0 fix(order): 修复优惠券和购物车相关功能的显示与交互问题 1 mēnesi atpakaļ
  zhangtao c682ef2293 fix(afterSalesDetail): 修复优惠券访问空指针异常 1 mēnesi atpakaļ
  zhangtao 48206df613 Merge remote-tracking branch 'origin/master' into zhangtao 1 mēnesi atpakaļ
  zhangtao e81f38adb2 feat(refund): 优化售后退款详情和优惠券展示 1 mēnesi atpakaļ
  zouzexu d5b0aef471 ``` 1 mēnesi atpakaļ
  zouzexu 201d2eb21f Merge branch 'zzx' 1 mēnesi atpakaļ
  zhangtao a088743034 fix(config): 更新开发环境地址及页面登录状态 1 mēnesi atpakaļ
  zhangtao af4363bd74 Merge branch 'master' into zhangtao 1 mēnesi atpakaļ
  zhangtao 23ffcda769 fix(auth): 取消景点详情页的登录限制 1 mēnesi atpakaļ
  zhangtao 8b4c5d485a feat(coupon): 优化优惠券功能及支付流程 1 mēnesi atpakaļ
  zhangtao d384d9c940 fix(cart): 优化购物车功能及界面交互体验 1 mēnesi atpakaļ
  zhangtao 50daae5275 feat(coupon): 新增优惠券模块及购物车价格明细弹窗 1 mēnesi atpakaļ
  zhangtao e469f2afd5 feat(cart): 优化购物车价格明细显示与选择逻辑 1 mēnesi atpakaļ

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

@@ -1166,6 +1166,16 @@ namespace Api {
      * skuList
      */
     skuList: CartSkuVo[]
+    /**
+     * 金额
+     */
+    amount: number
+    /**
+     * 优惠券
+     */
+    coupon: number
+    couponName: string
+    orderCouponItemDTOS?: AppMemberCouponVO[]
     [property: string]: any
   }
   interface AppletOrderSkuVo {
@@ -4036,4 +4046,33 @@ namespace Api {
     [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
+  }
+
 }

+ 3 - 0
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'],

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

@@ -205,6 +205,23 @@ 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>> & {}
       >(
@@ -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<

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

@@ -49,6 +49,7 @@ declare global {
   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']
@@ -415,6 +416,7 @@ declare module 'vue' {
     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']>

+ 4 - 4
src/config/index.ts

@@ -7,13 +7,13 @@ const mapEnvVersion = {
   // 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.217:8080', // 黄
+  // develop: 'http://192.168.1.20: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: 'http://47.109.84.152:8081',
   // develop: 'https://5ed0f7cc.r9.vip.cpolar.cn',
-  // develop: 'https://smqjh.api.zswlgz.com',
+  develop: 'https://smqjh.api.zswlgz.com',
   /**
    * 体验版
    */

+ 25 - 0
src/pages.json

@@ -120,6 +120,14 @@
             "navigationBarTitleText": "提交订单"
           }
         },
+        {
+          "path": "coupon/index",
+          "name": "xsb-coupon",
+          "islogin": true,
+          "style": {
+            "navigationBarTitleText": "优惠券列表"
+          }
+        },
         {
           "path": "goods/index",
           "name": "xsb-goods",
@@ -286,6 +294,14 @@
             "navigationBarTitleText": "详情"
           }
         },
+        {
+          "path": "giveawaysVip/giveawaysVip",
+          "name": "smqjh-giveaways-vip",
+          "islogin": false,
+          "style": {
+            "navigationBarTitleText": "选择赠品"
+          }
+        },
         {
           "path": "order/index",
           "name": "smqjh-order",
@@ -293,6 +309,15 @@
           "style": {
             "navigationBarTitleText": "订单列表"
           }
+        },
+        {
+          "path": "userVip/userVip",
+          "name": "smqjh-user-vip",
+          "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>

+ 12 - 0
src/pages/index/index.vue

@@ -102,6 +102,7 @@ onShareAppMessage(() => {
   }
 })
 const showoverlay = ref(false)
+const showoverlay2 = ref(true)
 async function getSelectZhUser() {
   const res = await Apis.sys.selectZhUser({})
   if (res.data.channelId === 54 && !res.data.isClaimed) {
@@ -327,6 +328,17 @@ function handleJyBanner() {
             </view>
           </view>
         </wd-overlay>
+        <wd-overlay :show="showoverlay2" @click="showoverlay2 = false">
+          <view class="mt-280rpx flex items-center justify-center">
+            <view class="relative h-906rpx w-644rpx flex flex-col items-center justify-center text-center" :style="{ backgroundImage: `url(${StaticUrl}/vip-index-popup.png)`, backgroundSize: 'cover', backgroundPosition: 'center' }">
+              <view class="absolute top-120rpx w-406rpx border-[1rpx_solid_#FFFFFF] rounded-50rpx border-solid px-30rpx py-8rpx text-center text-24rpx text-#FFF">
+                您的会员权益已生效有效期至2027-04-08 23:59:59
+              </view>
+              <image :src="`${StaticUrl}/vip-index-check.png`" class="absolute top-690rpx h-84rpx w-372rpx" @click="router.push({ name: 'smqjh-user-vip' })" />
+              <image :src="`${StaticUrl}/vip-index-shopping.png`" class="absolute top-800rpx h-84rpx w-288rpx" @click="router.push({ name: 'xsb-homeTabbar' })" />
+            </view>
+          </view>
+        </wd-overlay>
       </view>
     </view>
   </view>

+ 7 - 1
src/pages/my/index.vue

@@ -59,7 +59,13 @@ function handleGo(item: { name: string, status: string }) {
         </template>
         <template v-else>
           <view class="flex items-center">
-            <image :src="getUserAvatar" alt="" class="h-100rpx w-100rpx flex-shrink-0 rounded-full" />
+            <view class="relative" @click="router.push({ name: 'smqjh-user-vip' })">
+              <image :src="`${StaticUrl}/user-head-vip.png`" class="absolute right-[-20rpx] top-[-20rpx] h-50rpx w-50rpx" />
+              <image :src="getUserAvatar" alt="" class="h-100rpx w-100rpx flex-shrink-0 rounded-full" />
+              <view class="absolute left-[-6rpx] top-90rpx h-40rpx w-112rpx rounded-22rpx bg-[linear-gradient(132deg,#F0C568_0%,#FFF3B2_20.02%,#EBBA5E_44.19%,#FFF3B2_71.13%,#F0C26C_100%)] text-center text-24rpx text-#7F5935 line-height-40rpx">
+                尊贵会员
+              </view>
+            </view>
             <view class="ml-20rpx flex-1">
               <view class="text-32rpx font-semibold">
                 {{ userInfo.nickName }}

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

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

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

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

@@ -11,6 +11,7 @@ const _emit = defineEmits<{
 }>()
 const NodeList = ref<Api.DeliveryNode[]>([])
 const showNode = ref(false)
+
 const plugins = requirePlugin('logisticsPlugin')
 async function handleCancel(order: Api.xsbOrderList) {
   await useUserStore().handleCommonCancelOrder?.(order)
@@ -90,7 +91,7 @@ async function handleLogistics(_order: 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 }}

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

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

@@ -116,10 +116,15 @@ function handleGo(item: { name: string, status: 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 = Math.round(Number(unref(orderInfo)?.transfee) * 100) + Math.round(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>

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

@@ -113,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, shopId: unref(goodsInfo)?.shopId || 2 }] }) },
-  })
-  isLoging.value = false
 }
 async function handleAddCart() {
   if (Number(unref(goodsInfo)?.spuStock) < unref(SelectGoodsNum)) {

+ 18 - 1
src/subPack-xsb/orderDetaile/index.vue

@@ -468,6 +468,23 @@ function handleRefundDetail(item: any) {
             ¥{{ 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">
             积分
@@ -479,7 +496,7 @@ function handleRefundDetail(item: any) {
         <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)) ? '需' : '实'

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

+ 3 - 0
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" |
@@ -25,7 +26,9 @@ interface NavigateToOptions {
        "/subPack-common/revalueSuccess/index" |
        "/subPack-common/user-center/index" |
        "/subPack-smqjh/bannerDetaile/index" |
+       "/subPack-smqjh/giveawaysVip/giveawaysVip" |
        "/subPack-smqjh/order/index" |
+       "/subPack-smqjh/userVip/userVip" |
        "/subPack-film/choose-film/index" |
        "/subPack-film/choose-seat/index" |
        "/subPack-film/index/index" |

+ 18 - 0
src/utils/index.ts

@@ -239,3 +239,21 @@ export function fixImgStyle(html: string) {
 
   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] : ''
+}