Explorar o código

feat(api): 添加购物车相关API接口定义

添加了购物车查询、添加、删除等接口定义,包括myShoppingCart、
addShoppingCart、deleteShoppingCart三个接口,并定义了相关的
数据类型myShoppingCart和CartSkuVo

---

feat(cart): 实现购物车功能

新增购物车相关API调用,实现购物车查询、添加商品到购物车功能,
优化商品详情页加入购物车逻辑

---

fix(shop): 修复店铺选择逻辑

优化店铺选择逻辑,根据用户位置自动选择最近的可配送店铺,
修复商品详情页店铺ID传递问题

---

refactor(login): 优化登录后路由跳转逻辑

调整登录成功后的路由跳转逻辑,支持参数传递和路径解析

---

chore(config): 更新开发环境配置

启用新的开发环境地址,注释掉旧的配置项

---

refactor(types): 优化类型定义和格式化代码

修复API类型定义中的可选参数问题,格式化全局类型定义文件
```
zhangtao hai 1 día
pai
achega
b8e5b9970b

+ 81 - 1
src/api/api.type.d.ts

@@ -468,7 +468,7 @@ namespace Api {
     /**
      * 店铺id
      */
-    shopId?: number
+    shopId: number
     /**
      * 店铺所在纬度(可修改)
      * 店铺所在纬度
@@ -518,5 +518,85 @@ namespace Api {
     userId?: string
     [property: string]: any
   }
+  interface myShoppingCart {
+    /**
+     * 平台
+     */
+    platformId?: number
+    /**
+     * 经营状态(0:停业,1正常)
+     */
+    runStatus?: number
+    /**
+     * 门店id
+     */
+    shopId?: number
+    /**
+     * 门店名称
+     */
+    shopName?: string
+    /**
+     * 营业状态(0.否 1.是)
+     */
+    shopStatus?: number
+    /**
+     * skuList
+     */
+    skuList?: CartSkuVo[]
+    [property: string]: any
+  }
+  interface CartSkuVo {
+    createTime?: string
+    /**
+     * 主键
+     */
+    id?: number
+    /**
+     * 0 正常 1 已被删除
+     */
+    isDelete?: string
+    /**
+     * 会员id
+     */
+    memberId?: number
+    /**
+     * 数量
+     */
+    num?: number
+    /**
+     * pic
+     */
+    pic?: string
+    /**
+     * 平台
+     */
+    platformId?: number
+    /**
+     * price
+     */
+    price?: string
+    /**
+     * 门店
+     */
+    shopId?: number
+    /**
+     * 门店库存
+     */
+    shopSkuStocks?: string
+    /**
+     * sku_id
+     */
+    skuId?: number
+    /**
+     * 门店
+     */
+    skuName?: string
+    /**
+     * spec
+     */
+    spec?: string
+    updateTime?: string
+    [property: string]: any
+  }
 
 }

+ 3 - 0
src/api/apiDefinitions.ts

@@ -42,4 +42,7 @@ export default {
   'xsb.appAdvertInfo':['GET', '/smqjh-system/app-api/v1/appAdvertInfo'],
   'xsb.SearchTerm':['GET', '/smqjh-system/app-api/v1/SearchTerm/find'],
   'xsb.shopList':['GET', '/smqjh-pms/app-api/v1/shop/list'],
+  'common.myShoppingCart':['GET', '/smqjh-oms/app-api/v1/shoppingCart/myShoppingCart'],
+  'common.addShoppingCart':['POST', '/smqjh-oms/app-api/v1/shoppingCart/addShoppingCart'],
+  'common.deleteShoppingCart':['DELETE', '/smqjh-oms/app-api/v1/shoppingCart/{ids}'],
 };

+ 66 - 19
src/api/globals.d.ts

@@ -123,54 +123,54 @@ declare global {
       updateUserInfo<
         Config extends Alova2MethodConfig<any> & {
           pathParams: { memberId: number };
-          data:Api.userInfo;
+          data: Api.userInfo;
         }
-        >(
+      >(
         config: Config
       ): Alova2Method<any, 'sys.updateUserInfo', Config>;
       uploadFile<
-        Config extends Alova2MethodConfig<{url:string}>&{
+        Config extends Alova2MethodConfig<{ url: string }> & {
           data: {
             name: string;
             filePath: string;
           };
         }
-        >(
+      >(
         config: Config
-      ): Alova2Method<{url:string}, 'sys.uploadFile', Config>;
+      ): Alova2Method<{ url: string }, 'sys.uploadFile', Config>;
       addresses<
-        Config extends Alova2MethodConfig<Api.addressList[]>&{
+        Config extends Alova2MethodConfig<Api.addressList[]> & {
 
         }
-        >(
+      >(
         config: Config
       ): Alova2Method<Api.addressList[], 'sys.addresses', Config>;
       Addaddresses<
         Config extends Alova2MethodConfig<any> & {
-          data:Api.addressList;
+          data: Api.addressList;
         }
-        >(
+      >(
         config: Config
       ): Alova2Method<any, 'sys.Addaddresses', Config>;
       deleteAddresses<
         Config extends Alova2MethodConfig<any> & {
           pathParams: { ids: string };
         }
-        >(
+      >(
         config: Config
       ): Alova2Method<any, 'sys.deleteAddresses', Config>;
       updateAddresses<
         Config extends Alova2MethodConfig<any> & {
-          data:Api.addressList;
+          data: Api.addressList;
         }
-        >(
+      >(
         config: Config
       ): Alova2Method<any, 'sys.updateAddresses', Config>;
       addressesDetail<
         Config extends Alova2MethodConfig<Api.addressList> & {
           pathParams: { addressId: number };
         }
-        >(
+      >(
         config: Config
       ): Alova2Method<Api.addressList, 'sys.addressesDetail', Config>;
     }
@@ -190,7 +190,7 @@ declare global {
           data: {
             categoryId: number;
             shopId: number
-            channelId:number
+            channelId: number
           };
         }
       >(
@@ -201,7 +201,7 @@ declare global {
           data: {
             id: number;
             shopId: number
-            channelId:number
+            channelId: number
           };
         }
       >(
@@ -247,8 +247,8 @@ declare global {
              * 销量排序不传时为空,默认不排 ASC:价格升序,DESC:价格倒序
              */
             salesNum?: string;
-            shopId:number
-            channelId:number
+            shopId: number
+            channelId: number
           };
         }
       >(
@@ -262,8 +262,8 @@ declare global {
       ): Alova2Method<Api.xsbAdvertInfo[], 'xsb.appAdvertInfo', Config>;
       SearchTerm<
         Config extends Alova2MethodConfig<Api.xsbSearchTerm[]> & {
-          data:{
-            type:number
+          data: {
+            type: number
           }
         }
       >(
@@ -276,6 +276,53 @@ declare global {
         config: Config
       ): Alova2Method<Api.xsbShopList[], 'xsb.shopList', Config>;
     }
+    common: {
+      myShoppingCart<
+        Config extends Alova2MethodConfig<Api.myShoppingCart> & {
+          data:{
+            businessType:string;
+            channelId:number;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<Api.myShoppingCart, 'common.myShoppingCart', Config>;
+      addShoppingCart<
+        Config extends Alova2MethodConfig<Api.addShoppingCart> & {
+          data: {
+            /**
+              * 业务类型
+            */
+            businessType: string;
+            /**
+             * 渠道
+             */
+            channelId: number;
+            /**
+             * 数量
+             */
+            num: number;
+            /**
+             * 门店
+             */
+            shopId: number;
+            /**
+             * sku_id
+             */
+            skuId: number;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<Api.addShoppingCart, 'common.addShoppingCart', Config>;
+      deleteShoppingCart<
+        Config extends Alova2MethodConfig<Api.deleteShoppingCart> & {
+          pathParams: { ids: string };
+        }
+      >(
+        config: Config
+      ): Alova2Method<Api.deleteShoppingCart, 'common.deleteShoppingCart', Config>;
+    }
 
   }
 

+ 2 - 2
src/config/index.ts

@@ -13,9 +13,9 @@ const mapEnvVersion = {
    */
   // develop: 'http://192.168.1.166:8080', // 张
   // develop: 'http://192.168.1.101:8080',
-  // develop: 'http://192.168.0.157:8080',
+  develop: 'http://192.168.0.157:8080',
   // develop: 'http://192.168.1.253:8080',
-  develop: 'http://192.168.1.89:8080', // 田
+  // develop: 'http://192.168.1.89:8080', // 田
   // develop: 'http://47.109.84.152:8081',
   /**
    * 体验版

+ 5 - 2
src/pages/login/index.vue

@@ -26,12 +26,15 @@ async function handleGetPhone(e: UniHelper.ButtonOnGetphonenumberDetail) {
       await send(res.code, e.code)
       token.value = `Bearer ${data.value.access_token}`
       useGlobalToast().show({ msg: '登录成功' })
+      const path = redirectName.value.split('-')
+      console.log(path, '路劲')
+
       setTimeout(() => {
         if (tabList.includes(redirectName.value)) {
-          router.pushTab({ path: redirectName.value })
+          router.pushTab({ path: path[0] })
         }
         else {
-          router.replace({ path: redirectName.value })
+          router.replace({ path: path[0], params: path[1] as unknown as Record<string, string> })
         }
         useUserStore().getUserInfo()
       }, 2000)

+ 1 - 1
src/router/index.ts

@@ -33,7 +33,7 @@ router.beforeEach((to, from, next) => {
   console.log('🚀 beforeEach 守卫触发:', { to, from }, '')
   const { token, redirectName } = storeToRefs(useUserStore())
   if (to.name === 'smqjh-login') {
-    redirectName.value = String(from.path)
+    redirectName.value = `${String(from.path)}-${to.params}`
   }
   if (!whitePathName().includes(to.name) && !token.value) {
     const { confirm: showConfirm } = useGlobalMessage()

+ 13 - 0
src/subPack-xsb/commonTab/components/cart.vue

@@ -10,8 +10,21 @@ definePage({
     disableScroll: true,
   },
 })
+const { userInfo, token } = storeToRefs(useUserStore())
+const { data: cartList, send } = useRequest(() => Apis.common.myShoppingCart({
+  data: {
+    channelId: unref(userInfo).channelId,
+    businessType: 'XSB',
+  },
+}), { immediate: false })
 const isAll = ref(false)
 const selectCatrt = ref()
+onMounted(() => {
+  if (unref(token)) {
+    send()
+  }
+  console.log(cartList.value)
+})
 </script>
 
 <template>

+ 0 - 1
src/subPack-xsb/commonTab/components/index.vue

@@ -90,7 +90,6 @@ watch(() => backTop.value, () => {
       </template>
     </wd-navbar>
     <scroll-view
-
       :lower-threshold="80"
       scroll-y enable-passive scroll-anchoring :scroll-top="scrollTop" class="content ios" @scroll="handleScroll" @scrolltolower="emit('scrollBottom')"
     >

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

@@ -69,7 +69,7 @@ onLoad((options: any) => {
 })
 const loading = ref(true)
 async function getCategories() {
-  const res = await Apis.xsb.categories({ data: { shopId: 1, channelId: 1 } })
+  const res = await Apis.xsb.categories({ data: { shopId: unref(SelectShopInfo).shopId, channelId: unref(userInfo).channelId || 1 } })
   commonCategoryData.value = res
   topNavActive.value = res[0].code
   leftActive.value = res[0].children[0].code
@@ -84,6 +84,7 @@ async function getSearchData(type: number) {
 }
 
 onMounted(async () => {
+  await useSysXsbStore().getAllShopList()
   getCurrentImg()
   getCategories()
   hotText.value = await getSearchData(2)

+ 7 - 3
src/subPack-xsb/goods/index.vue

@@ -85,7 +85,7 @@ function handleGoCurren(id: number) {
 }
 
 async function getGoodsDetaile() {
-  const res = await Apis.xsb.getProductDetail({ data: { id: goodsId.value, shopId: Number(SelectShopInfo.value?.shopId) || 1, channelId: userInfo.value.channelId || 1 } })
+  const res = await Apis.xsb.getProductDetail({ data: { id: goodsId.value, shopId: Number(SelectShopInfo.value?.shopId), channelId: userInfo.value.channelId || 1 } })
   console.log(res, '请求')
   goodsInfo.value = res
   specId.value = res.skuList?.[0].skuId
@@ -97,6 +97,10 @@ function handleConfimOrder() {
   }
   router.push({ name: 'xsb-confirmOrder' })
 }
+async function handleAddCart() {
+  await useXsbCartStore().addCart(specId.value, unref(SelectGoodsNum))
+  selectGoods.value = false
+}
 </script>
 
 <template>
@@ -114,7 +118,7 @@ function handleConfimOrder() {
           </view>
         </template>
       </wd-swiper>
-      <view class="header view-0 relative z-3 rounded-t-32rpx px24rpx pt24rpx -mt30rpx">
+      <view class="view-0 header relative z-3 rounded-t-32rpx px24rpx pt24rpx -mt30rpx">
         <view class="flex items-center justify-between">
           <view class="flex items-end text-#FF4D3A font-semibold">
             <view class="text-24rpx">
@@ -372,7 +376,7 @@ function handleConfimOrder() {
         <view class="shadow-fixed ios fixed bottom-0 left-0 box-border w-full rounded-t-32rpx bg-white px24rpx">
           <view class="box-border w-full flex items-center justify-between py20rpx">
             <view class="w-48%">
-              <wd-button plain hairline block>
+              <wd-button plain hairline block @click="handleAddCart">
                 加入购物车
               </wd-button>
             </view>

+ 10 - 48
src/subPack-xsb/selectAddress/index.vue

@@ -9,61 +9,23 @@ definePage({
     navigationBarTitleText: '选择收获地址',
   },
 })
-const { SelectShopInfo } = storeToRefs(useSysXsbStore())
-const { name, Location } = storeToRefs(useAddressStore())
-const nearbyAddress = ref<Api.xsbShopList>()
+const { SelectShopInfo, xsbShopList: shopList } = storeToRefs(useSysXsbStore())
+const { name } = storeToRefs(useAddressStore())
 
-const { data: shopList } = useRequest(() => Apis.xsb.shopList({})).onSuccess(() => {
-  if (!Location.value.longitude || !Location.value.latitude || !shopList.value) {
-    return null
-  }
-
-  const getDistance = (lat1: number, lng1: number, lat2: number, lng2: number): number => {
-    const R = 6371 // 地球半径(公里)
-    const dLat = (lat2 - lat1) * Math.PI / 180
-    const dLng = (lng2 - lng1) * Math.PI / 180
-    const a
-      = Math.sin(dLat / 2) * Math.sin(dLat / 2)
-        + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180)
-        * Math.sin(dLng / 2) * Math.sin(dLng / 2)
-    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
-    return R * c // 距离(公里)
-  }
-
-  let minDistance = Infinity
-
-  for (const shop of shopList.value) {
-    if (shop.shopLat && shop.shopLng) {
-      const distance = getDistance(
-        Location.value.latitude,
-        Location.value.longitude,
-        Number(shop.shopLat),
-        Number(shop.shopLng),
-      )
-
-      if (distance < minDistance) {
-        minDistance = distance
-        nearbyAddress.value = { ...shop, distance: Number(distance.toFixed(2)) }
-      }
-    }
-  }
-})
 const selectAddressId = ref()
 
 const allShopList = computed(() => {
-  return shopList.value?.filter(it => it.shopId !== nearbyAddress.value?.shopId)
+  return shopList.value?.filter(it => it.shopId !== SelectShopInfo.value?.shopId)
+})
+
+onMounted(() => {
+  useSysXsbStore().getAllShopList()
 })
 
 function handleClick(item: Api.xsbShopList) {
   SelectShopInfo.value = item
   router.back()
 }
-
-onUnload(() => {
-  if (!SelectShopInfo.value?.shopId) {
-    SelectShopInfo.value = nearbyAddress.value
-  }
-})
 </script>
 
 <template>
@@ -76,7 +38,7 @@ onUnload(() => {
         <view class="flex items-center">
           <image :src="`${StaticUrl}/location-black.png`" class="h33.8rpx min-w28.97rpx w28.97rpx" />
           <view class="ml18rpx max-w-380rpx truncate text-32rpx">
-            {{ name }}
+            {{ name == '请选择位置' ? SelectShopInfo.shopName : name }}
           </view>
         </view>
         <view class="flex items-center text-24rpx text-#1A8DFF" @click="useAddressStore().getMapAddress">
@@ -111,12 +73,12 @@ onUnload(() => {
           </wd-radio-group>
         </view>
       </view>
-      <view v-if="nearbyAddress" class="mt20rpx rounded-16rpx bg-white px24rpx py28rpx" @click="handleClick(nearbyAddress)">
+      <view v-if="SelectShopInfo" class="mt20rpx rounded-16rpx bg-white px24rpx py28rpx" @click="handleClick(SelectShopInfo)">
         <view class="text-24rpx text-#AAAAAA">
           附近的门店
         </view>
         <view class="mt24rpx text-28rpx font-semibold">
-          {{ nearbyAddress?.shopName }}
+          {{ SelectShopInfo?.shopName }}
         </view>
         <view class="mt16rpx text-24rpx text-#AAAAAA">
           以为您选择距离最近的可配送店

+ 34 - 1
src/subPack-xsb/store-xsb/cart.ts

@@ -1,4 +1,5 @@
 import { defineStore } from 'pinia'
+import router from '@/router'
 
 interface cartState {
   cartList: Api.xsbCategoryProductList[]
@@ -8,6 +9,38 @@ export const useXsbCartStore = defineStore('xsb-cart', {
     cartList: [],
   }),
   actions: {
-
+    /**
+     * 星闪豹加入购物车逻辑
+     * @param skuId 商品skuId
+     * @param num 商品数量
+     */
+    async addCart(skuId: number, num: number) {
+      return new Promise((resolve, reject) => {
+        if (!skuId) {
+          useGlobalToast().show({ msg: '请选择商品规格' })
+          return reject(new Error('请选择商品规格'))
+        }
+        const { userInfo, token } = storeToRefs(useUserStore())
+        if (!token.value) {
+          useGlobalToast().show({ msg: '请先登录' })
+          setTimeout(() => {
+            router.replace({ name: 'smqjh-login' })
+          }, 2000)
+          return reject(new Error('请先登录'))
+        }
+        const { SelectShopInfo } = storeToRefs(useSysXsbStore())
+        Apis.common.addShoppingCart({
+          data: {
+            businessType: 'XSB',
+            skuId,
+            num,
+            shopId: SelectShopInfo.value?.shopId,
+            channelId: userInfo.value.channelId,
+          },
+        }).then((res) => {
+          resolve(res)
+        })
+      })
+    },
   },
 })

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

@@ -18,7 +18,12 @@ interface SysState {
   /**
    * 店铺名称
    */
-  SelectShopInfo: Api.xsbShopList | undefined
+  SelectShopInfo: Api.xsbShopList
+  /**
+   * 店铺列表
+   */
+
+  xsbShopList: Api.xsbShopList[]
 
 }
 export const useSysXsbStore = defineStore('system-xsb', {
@@ -28,7 +33,8 @@ export const useSysXsbStore = defineStore('system-xsb', {
     leftActive: '',
     searchList: [],
     backTop: false,
-    SelectShopInfo: {},
+    SelectShopInfo: { shopId: 0 },
+    xsbShopList: [],
   }),
   actions: {
     getTabbarItemValue(name: string) {
@@ -36,6 +42,44 @@ export const useSysXsbStore = defineStore('system-xsb', {
 
       return 0
     },
+    async getAllShopList() {
+      const { Location } = storeToRefs(useAddressStore())
+      if (Location.value.latitude == null || Location.value.longitude == null) {
+        useAddressStore().getLocation()
+        return
+      }
+      const res = await Apis.xsb.shopList({})
+      this.xsbShopList = res
+
+      const getDistance = (lat1: number, lng1: number, lat2: number, lng2: number): number => {
+        const R = 6371 // 地球半径(公里)
+        const dLat = (lat2 - lat1) * Math.PI / 180
+        const dLng = (lng2 - lng1) * Math.PI / 180
+        const a
+      = Math.sin(dLat / 2) * Math.sin(dLat / 2)
+        + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180)
+        * Math.sin(dLng / 2) * Math.sin(dLng / 2)
+        const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
+        return R * c // 距离(公里)
+      }
+      let minDistance = Infinity
+      for (const shop of res) {
+        if (shop.shopLat && shop.shopLng) {
+          const distance = getDistance(
+            Location.value.latitude,
+            Location.value.longitude,
+            Number(shop.shopLat),
+            Number(shop.shopLng),
+          )
+
+          if (distance < minDistance) {
+            minDistance = distance
+            // nearbyAddress.value = { ...shop, distance: Number(distance.toFixed(2)) }
+            this.SelectShopInfo = { ...shop, distance: Number(distance.toFixed(2)) }
+          }
+        }
+      }
+    },
 
   },
 })