Просмотр исходного кода

feat(api): 新增小商城相关接口类型定义与调用配置

- 添加了商品分类、商品列表、商品详情及用户积分等接口的 TypeScript 类型定义
- 配置了新增 API 的请求方法,包括分类查询、商品列表、商品详情和积分记录分页查询
- 更新全局 API 响应处理逻辑,统一返回 data 字段并优化错误提示信息
- 在自动导入声明中注册 xsb 相关 store 和组件
- 实现积分页面的数据绑定与滚动加载功能
- 完善商品分类页面的交互逻辑,支持侧边栏联动和商品列表动态加载
- 调整部分 UI 样式与布局以适配新功能展示需求
zhangtao 2 недель назад
Родитель
Сommit
2f4f85fdf8

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

@@ -0,0 +1,173 @@
+namespace Api {
+
+  interface xsbCategoriesChildren {
+    id: number
+    name: string
+    shopId: number
+    code: string
+    parentCode: string
+    level: number
+    icon: string
+    children: xsbCategoriesChildren[]
+  }
+  interface xsbCategories {
+    id: number
+    name: string
+    shopId: number
+    code: string
+    parentCode: string
+    level: number
+    icon: string
+    children: xsbCategoriesChildren[]
+  }
+  interface xsbCategoryProductList {
+    /**
+     * 分类id
+     */
+    categoryId?: number
+    /**
+     * 渠道对应商品售价
+     */
+    channelProdPrice?: number
+    id?: string
+    /**
+     * 商品主图
+     */
+    pic?: string
+    /**
+     * 商品名称
+     */
+    prodName?: string
+    /**
+     * 门店id
+     */
+    shopId?: number
+    /**
+     * 销量
+     */
+    soldNum?: number
+    /**
+     * 规格
+     */
+    spec?: string
+    [property: string]: any
+  }
+  interface xsbProductDetail {
+    /**
+     * 品牌名称
+     */
+    brandName?: string
+    /**
+     * 分类id
+     */
+    categoryId?: number
+    /**
+     * 渠道对应商品售价
+     */
+    channelProdPrice?: number
+    /**
+     * 详细描述
+     */
+    content?: string
+    id?: string
+    /**
+     * 商品图片
+     */
+    imgs?: string
+    /**
+     * 商品主图
+     */
+    pic?: string
+    /**
+     * 商品名称
+     */
+    prodName?: string
+    /**
+     * 门店id
+     */
+    shopId?: number
+    /**
+     * 销量
+     */
+    soldNum?: number
+    /**
+     * 规格
+     */
+    spec?: string
+    /**
+     * 重量
+     */
+    weight?: number
+    /**
+     * 单位
+     */
+    weightUnit?: string
+    [property: string]: any
+  }
+  interface xsbFindUserPointsPage {
+    /**
+     * 渠道(企业ID)
+     */
+    channelId?: number
+    /**
+     * 编码
+     */
+    code?: string
+    /**
+     * 创建日期
+     */
+    creationDate?: string
+    /**
+     * 当前可用积分
+     */
+    currentlyAvailablePoints?: number
+    /**
+     * 过期日期
+     */
+    expiryDate?: string
+    /**
+     * 过期积分
+     */
+    expiryPoints?: number
+    /**
+     * 订单编号
+     */
+    orderNumber?: string
+    /**
+     * 充值积分ID
+     */
+    pointsId?: number
+    /**
+     * 积分记录类型(1-充值,2-下单,3-退款, 4-过期积分(只包含每天可用的过期),5-退款时过期积分)
+     */
+    pointsType?: number
+    /**
+     * 用户ID
+     */
+    userId?: number
+    /**
+     * 变动积分
+     */
+    variablePoints?: number
+    [property: string]: any
+  }
+  interface xsbFindUserPoints {
+    /**
+     * 总可用积分
+     */
+    availablePointsTotal?: number
+    /**
+     * 总消耗积分
+     */
+    consumePointsTotal?: number
+    /**
+     * 总可用过期积分
+     */
+    expiryPointsTotal?: number
+    /**
+     * 总充值积分
+     */
+    pointsTotal?: number
+    [property: string]: any
+  }
+}

+ 5 - 0
src/api/apiDefinitions.ts

@@ -25,4 +25,9 @@ Some useful links:
  */
 export default {
   'sys.auth': ['POST', '/smqjh-auth/oauth2/token'],
+  'xsb.categories':['GET', '/smqjh-pms/app-api/v1/categories'],
+  'xsb.getCategoryProductList':['GET', '/smqjh-pms/app-api/v1/spu/getCategoryProductList'],
+  'xsb.getProductDetail':['GET', '/smqjh-pms/app-api/v1/spu/getProductDetails'],
+  'xsb.findUserPointsPage':['PUT', '/smqjh-system/app-api/v1/points/findUserPointsPage'],
+  'xsb.findUserPoints':['GET', '/smqjh-system/app-api/v1/points/findUserPoints'],
 };

+ 2 - 2
src/api/core/handlers.ts

@@ -46,7 +46,7 @@ export async function handleAlovaResponse(
 
   // Handle HTTP error status codes
   if (statusCode >= 400) {
-    globalToast.error(`Request failed with status: ${statusCode}`)
+    globalToast.error(`服务器异常!请稍后再试: ${statusCode}`)
     throw new ApiError(`Request failed with status: ${statusCode}`, statusCode, data)
   }
 
@@ -58,7 +58,7 @@ export async function handleAlovaResponse(
   }
 
   // Return data for successful responses
-  return json
+  return json.data
 }
 
 // Handle request errors

+ 54 - 3
src/api/globals.d.ts

@@ -102,17 +102,67 @@ export interface ApiResponse {
   msg?: string;
 }
 
-
+interface listData<T> {
+  list: T[];
+}
 declare global {
   interface Apis {
     sys: {
       auth<
-        Config extends Alova2MethodConfig<{data:logoinToken}> & {
+        Config extends Alova2MethodConfig<logoinToken> & {
           params: loginModel;
         }
       >(
         config: Config
-      ): Alova2Method<{data:logoinToken}, 'sys.auth', Config>;
+      ): Alova2Method<logoinToken, 'sys.auth', Config>;
+    }
+    xsb: {
+      categories<
+        Config extends Alova2MethodConfig<Api.xsbCategories []> & {
+          data: {
+            channelId: number;
+            shopId: number
+          };
+        }
+      >(
+        config: Config
+      ): Alova2Method<Api.xsbCategories [], 'xsb.categories', Config>;
+      getCategoryProductList<
+        Config extends Alova2MethodConfig<Api.xsbCategoryProductList []> & {
+          data: {
+            categoryId: number;
+            shopId: number
+          };
+        }
+      >(
+        config: Config
+      ): Alova2Method<Api.xsbCategoryProductList [], 'xsb.getCategoryProductList', Config>;
+      getProductDetail<
+        Config extends Alova2MethodConfig< Api.xsbProductDetail> & {
+          data: {
+            id: number;
+          };
+        }
+      >(
+        config: Config
+      ): Alova2Method< Api.xsbProductDetail, 'xsb.getProductDetail', Config>;
+      findUserPoints<
+        Config extends Alova2MethodConfig<Api.xsbFindUserPoints> & {
+
+        }
+      >(
+        config: Config
+      ): Alova2Method<Api.xsbFindUserPoints, 'xsb.findUserPoints', Config>;
+      findUserPointsPage<
+        Config extends Alova2MethodConfig<listData<Api.xsbFindUserPointsPage>> & {
+          data: {
+            pageNum: number;
+            pageSize: number;
+          };
+        }
+      >(
+        config: Config
+      ): Alova2Method<listData<Api.xsbFindUserPointsPage>, 'xsb.findUserPointsPage', Config>;
     }
 
   }
@@ -138,3 +188,4 @@ export interface logoinToken {
   refresh_token: string
   token_type: string
 }
+

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

@@ -334,6 +334,7 @@ declare global {
   const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus']
   const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
   const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
+  const useXsbCartStore: typeof import('./subPack-xsb/store-xsb/cart')['useXsbCartStore']
   const useXsbTabbar: typeof import('./composables/useXsbTabbar')['useXsbTabbar']
   const useXsbTabbarStore: typeof import('./subPack-xsb/store-xsb/tabbar')['useXsbTabbarStore']
   const watch: typeof import('vue')['watch']
@@ -690,7 +691,7 @@ declare module 'vue' {
     readonly useWindowFocus: UnwrapRef<typeof import('@vueuse/core')['useWindowFocus']>
     readonly useWindowScroll: UnwrapRef<typeof import('@vueuse/core')['useWindowScroll']>
     readonly useWindowSize: UnwrapRef<typeof import('@vueuse/core')['useWindowSize']>
-    readonly useXsbTabbar: UnwrapRef<typeof import('./composables/useXsbTabbar')['useXsbTabbar']>
+    readonly useXsbCartStore: UnwrapRef<typeof import('./subPack-xsb/store-xsb/cart')['useXsbCartStore']>
     readonly watch: UnwrapRef<typeof import('vue')['watch']>
     readonly watchArray: UnwrapRef<typeof import('@vueuse/core')['watchArray']>
     readonly watchAtMost: UnwrapRef<typeof import('@vueuse/core')['watchAtMost']>

+ 2 - 0
src/components.d.ts

@@ -24,6 +24,7 @@ declare module 'vue' {
     WdIcon: typeof import('wot-design-uni/components/wd-icon/wd-icon.vue')['default']
     WdInput: typeof import('wot-design-uni/components/wd-input/wd-input.vue')['default']
     WdInputNumber: typeof import('wot-design-uni/components/wd-input-number/wd-input-number.vue')['default']
+    WdLoadmore: typeof import('wot-design-uni/components/wd-loadmore/wd-loadmore.vue')['default']
     WdMessageBox: typeof import('wot-design-uni/components/wd-message-box/wd-message-box.vue')['default']
     WdNavbar: typeof import('wot-design-uni/components/wd-navbar/wd-navbar.vue')['default']
     WdNoticeBar: typeof import('wot-design-uni/components/wd-notice-bar/wd-notice-bar.vue')['default']
@@ -35,6 +36,7 @@ declare module 'vue' {
     WdSidebar: typeof import('wot-design-uni/components/wd-sidebar/wd-sidebar.vue')['default']
     WdSidebarItem: typeof import('wot-design-uni/components/wd-sidebar-item/wd-sidebar-item.vue')['default']
     WdSortButton: typeof import('wot-design-uni/components/wd-sort-button/wd-sort-button.vue')['default']
+    WdStatusTip: typeof import('wot-design-uni/components/wd-status-tip/wd-status-tip.vue')['default']
     WdStep: typeof import('wot-design-uni/components/wd-step/wd-step.vue')['default']
     WdSteps: typeof import('wot-design-uni/components/wd-steps/wd-steps.vue')['default']
     WdSwiper: typeof import('wot-design-uni/components/wd-swiper/wd-swiper.vue')['default']

+ 1 - 0
src/config/index.ts

@@ -11,6 +11,7 @@ const mapEnvVersion = {
   /**
    * 开发版
    */
+  // develop: 'http://192.168.1.166:8080', // 张
   develop: 'http://192.168.1.101:8080',
   // develop: 'http://192.168.0.157:8080',
   /**

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

@@ -29,6 +29,7 @@ const navList = ref([
 ])
 onMounted(() => {
   addressStore.getLocation()
+  ScrollDown.value = false
 })
 onPageScroll((e) => {
   if (e.scrollTop >= 80) {

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

@@ -23,7 +23,7 @@ async function handleGetPhone(e: UniHelper.ButtonOnGetphonenumberDetail) {
     success: async (res) => {
       uni.hideLoading()
       await send(res.code, e.code)
-      token.value = data.value.data.access_token
+      token.value = data.value.access_token
       useGlobalToast().show({ msg: '登录成功' })
       setTimeout(() => {
         router.replace({ name: redirectName.value })

+ 23 - 11
src/subPack-common/integral/index.vue

@@ -7,6 +7,17 @@ definePage({
     disableScroll: true,
   },
 })
+const { data: info } = useRequest(() =>
+  Apis.xsb.findUserPoints({}),
+)
+const type = ['充值', '下单', '退款', '过期积分', '退款过期积分']
+const { data: pointList, isLastPage, page } = usePagination((pageNum, pageSize) => Apis.xsb.findUserPointsPage({ data: { pageNum, pageSize } }), { data: resp => resp.list, initialPage: 1, initialPageSize: 10, immediate: true, append: true })
+
+function handleScrollBottom() {
+  if (!isLastPage.value) {
+    page.value++
+  }
+}
 </script>
 
 <template>
@@ -17,7 +28,7 @@ definePage({
           总充值积分
         </view>
         <view class="text-36rpx text-#222 font-semibold">
-          100
+          {{ info?.pointsTotal }}
         </view>
       </view>
       <view class="flex flex-col items-center justify-center">
@@ -25,7 +36,7 @@ definePage({
           当前可用积分
         </view>
         <view class="text-36rpx text-#222 font-semibold">
-          100
+          {{ info?.availablePointsTotal }}
         </view>
       </view>
       <view class="flex flex-col items-center justify-center">
@@ -33,7 +44,7 @@ definePage({
           已过期积分
         </view>
         <view class="text-36rpx text-#222 font-semibold">
-          100
+          {{ info?.expiryPointsTotal }}
         </view>
       </view>
       <view class="flex flex-col items-center justify-center">
@@ -41,34 +52,35 @@ definePage({
           已消耗积分
         </view>
         <view class="text-36rpx text-#222 font-semibold">
-          100
+          {{ info?.consumePointsTotal }}
         </view>
       </view>
     </view>
     <view class="px24rpx py20rpx">
       可用积分记录
     </view>
-    <scroll-view scroll-y class="view">
-      <view v-for="item in 20" :key="item" class="bg-white p24rpx">
+    <scroll-view scroll-y class="view" @scrolltolower="handleScrollBottom">
+      <view v-for="item, index in pointList" :key="item.pointsId" class="bg-white p24rpx">
         <view class="flex items-center justify-between text-32rpx font-semibold">
           <view class="text-#222">
-            购物
+            {{ type[Number(item.pointsType)] }}
           </view>
           <view class="text-#FF4A39">
-            -20
+            {{ item?.variablePoints }}
           </view>
         </view>
         <view class="mt20rpx flex items-center justify-between text-28rpx text-#AAAAAA">
-          <view>12月17日 17:06</view>
-          <view>当前可用积分 100</view>
+          <view>{{ item?.creationDate }}</view>
+          <view>当前可用积分 {{ item?.currentlyAvailablePoints || 0 }}</view>
         </view>
+        <view v-if="index < pointList.length - 1" class="mt24rpx h-2rpx w-full bg-#F0F0F0" />
       </view>
     </scroll-view>
   </view>
 </template>
 
 <style scoped>
-.view{
+.view {
   height: calc(100vh - var(--window-top) - 250rpx);
 }
 </style>

+ 1 - 1
src/subPack-smqjh/goodsList/index.vue

@@ -63,7 +63,7 @@ function handleSortList(id: number) {
         </view>
       </template>
     </wd-navbar>
-    <view class="sticky left-0 top-0 z-9 box-border h400rpx bg-#F4FFD1 pt20rpx" :style="{ paddingTop: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 12}px` }">
+    <view class="sticky left-0 top-0 z-9 box-border bg-#F4FFD1 pt20rpx" :style="{ paddingTop: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 12}px` }">
       <scroll-view scroll-x class="scrollx box-border w-full flex-shrink-0 whitespace-nowrap pl24rpx">
         <view class="flex items-end pb10rpx">
           <view v-for="item, idx in classfiylist" :key="idx" class="mr24rpx flex flex-col items-center justify-center" @click="handleTopNavChange(idx)">

+ 231 - 116
src/subPack-xsb/commonTab/components/classfiy.vue

@@ -1,22 +1,15 @@
 <script setup lang="ts">
+import type { LoadMoreState } from 'wot-design-uni/components/wd-loadmore/types'
 import { StaticUrl } from '@/config'
+import router from '@/router'
 
+const props = defineProps<{ categoryList: Api.xsbCategories[] }>()
 const { statusBarHeight, MenuButtonHeight } = storeToRefs(useSysStore())
-const classfiylist = ref([
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-])
-const topNavActive = ref(0)
+const { topNavActive, leftActive } = storeToRefs(useSysXsbStore())
+const classfiylist = computed(() => props.categoryList)
 const sortClassBtn = ref(1)
 const show = ref(false)
+const showball = ref(false)
 definePage({
   name: 'xsb-classfiy',
   islogin: false,
@@ -26,76 +19,111 @@ definePage({
     disableScroll: true,
   },
 })
+const isTopLoading = ref(false)
+const basllObj = ref({
+  left: 0,
+  top: 0,
+  x: 0,
+  y: 0,
+})
+const x = computed(() => `${basllObj.value.x}px`)
+const y = computed(() => `${basllObj.value.y}px`)
+const left = computed(() => `${basllObj.value.left}px`)
+const top = computed(() => `${basllObj.value.top}px`)
+const productList = ref<Api.xsbCategoryProductList[]>([])
+const topScrollView = ref()
+const scrollTop = ref(0)
+const goodsLoading = ref<LoadMoreState>()
 const navHeight = computed(() => {
   return (`${Number(MenuButtonHeight.value) + Number(statusBarHeight.value)}px`)
 })
-function handleTopNavChange(idx: number) {
-  topNavActive.value = idx
+function handleTopNavChange(item: Api.xsbCategoriesChildren) {
+  topNavActive.value = item.code
+  leftActive.value = item.children[0].code
+  show.value = false
+  topScrollView.value = null
+  nextTick(() => {
+    topScrollView.value = item.code
+  })
+  getProductList()
 }
 function handleOpen() {
   show.value = true
   console.log(navHeight.value, '打开')
 }
 
-const active = ref<number>(1)
-const scrollTop = ref<number>(0)
-const itemScrollTop = ref<number[]>([])
+const categories = computed(() => {
+  return classfiylist.value.find(it => it.code === topNavActive.value)?.children || []
+})
+const categoriesId = computed(() => {
+  return categories.value.find(it => it.code === leftActive.value)?.id
+})
 
-const subCategories = Array.from({ length: 24 }).fill({ title: '标题文字', label: '这是描述这是描述' }, 0, 24)
-const categories = ref([
-  {
-    label: '分类一',
-    title: '标题一',
-    items: subCategories,
-  },
-  {
-    label: '分类二',
-    title: '标题二',
-    items: subCategories,
-  },
-  {
-    label: '分类三',
-    title: '标题三',
-    items: subCategories.slice(0, 18),
-  },
-  {
-    label: '分类四',
-    title: '标题四',
-    items: subCategories.slice(0, 21),
-  },
-  {
-    label: '分类五',
-    title: '标题五',
-    items: subCategories,
-  },
-  {
-    label: '分类六',
-    title: '标题六',
-    items: subCategories.slice(0, 18),
-  },
-  {
-    label: '分类七',
-    title: '标题七',
-    items: subCategories,
-  },
-])
+function handleChange({ value }: { value: string }) {
+  leftActive.value = value
+  getProductList()
+}
+async function getProductList() {
+  const res = await Apis.xsb.getCategoryProductList({
+    data: {
+      categoryId: Number(categoriesId.value),
+      shopId: 1,
+    },
+  })
+  productList.value = res
+  goodsLoading.value = 'finished'
+  isTopLoading.value = false
+}
 
-function handleChange({ value }) {
-  active.value = value
-  scrollTop.value = itemScrollTop.value[value]
+function handlScrollBottom() {
+  goodsLoading.value = 'loading'
+  const index = categories?.value.findIndex(it => it.code === leftActive.value)
+  if (index !== -1) {
+    handleChange({ value: categories.value[index + 1].code })
+  }
 }
-function onScroll(e) {
-  const { scrollTop } = e.detail
-  const threshold = 50 // 下一个标题与顶部的距离
-  if (scrollTop < threshold) {
-    active.value = 0
+function handleSrollTop() {
+  isTopLoading.value = true
+  const index = categories?.value.findIndex(it => it.code === leftActive.value)
+  if (index !== -1) {
+    if (index === 0) {
+      nextTick(() => isTopLoading.value = false)
+    }
+    else {
+      handleChange({ value: categories.value[index - 1].code })
+    }
+  }
+}
+function handleAddCart(event: WechatMiniprogram.TouchEvent, item: Api.xsbCategoryProductList) {
+  console.log(event.detail.x, item)
+  if (showball.value)
     return
+  basllObj.value.left = event.detail.x
+  basllObj.value.top = event.detail.y
+  showball.value = true
+  setTimeout(() => {
+    showball.value = false
+  }, 500)
+}
+onMounted(() => {
+  if (leftActive.value) {
+    handleChange({ value: leftActive.value })
   }
-  const index = itemScrollTop.value.findIndex(top => top > scrollTop && top - scrollTop <= threshold)
-  if (index > -1) {
-    active.value = index
+  if (topNavActive.value) {
+    topScrollView.value = null
+    nextTick(() => {
+      topScrollView.value = topNavActive.value
+    })
   }
-}
+  const query = uni.createSelectorQuery().in(getCurrentInstance())
+  query.select('.cart-box').boundingClientRect()
+  query.exec((res) => {
+    basllObj.value.y = res[0].top
+    uni.getSystemInfo().then((info) => {
+      basllObj.value.x = -(info.screenWidth - res[0].left - 15)
+    })
+  })
+})
 </script>
 
 <template>
@@ -106,11 +134,16 @@ function onScroll(e) {
     >
       <template #left>
         <view class="flex items-center">
-          <view class="h60rpx w-474rpx flex items-center justify-between border-2rpx border-[var(--them-color)] rounded-40rpx border-solid bg-white pr6rpx">
+          <view
+            class="h60rpx w-474rpx flex items-center justify-between border-2rpx border-[var(--them-color)] rounded-40rpx border-solid bg-white pr6rpx"
+          >
             <view class="flex items-center pb14rpx pl24rpx pt16rpx">
               <wd-icon name="search" size="14" color="#ccc" />
               <view class="search ml12rpx h-full w-full overflow-hidden">
-                <wd-notice-bar :text="['霸王茶姬', '牛奶', '洗衣机']" custom-class="notice-bar" color="#ccc" background-color="#fff" direction="vertical" />
+                <wd-notice-bar
+                  :text="['霸王茶姬', '牛奶', '洗衣机']" custom-class="notice-bar" color="#ccc"
+                  background-color="#fff" direction="vertical"
+                />
               </view>
             </view>
             <view class="flex items-center pr28rpx">
@@ -127,40 +160,59 @@ function onScroll(e) {
       class="h162rpx flex items-center justify-between bg-#F4FFD1 pl24rpx"
       :style="{ paddingTop: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 12}px` }"
     >
-      <scroll-view scroll-x class="scrollx w-90% flex-shrink-0 whitespace-nowrap">
+      <scroll-view
+        scroll-x :scroll-into-view-offset="-150" scroll-with-animation enable-passive
+        class="scrollx w-90% flex-shrink-0 whitespace-nowrap" :scroll-into-view="`id${topScrollView}`"
+      >
         <view class="flex items-end pb10rpx">
-          <view v-for="item, idx in classfiylist" :key="idx" class="mr24rpx flex flex-col items-center justify-center" @click="handleTopNavChange(idx)">
+          <view
+            v-for="item in classfiylist" :id="`id${item.code}`" :key="item.code"
+            class="mr24rpx flex flex-col items-center justify-center" @click="handleTopNavChange(item)"
+          >
             <image
               :src="item.icon"
-              :class="[topNavActive == idx ? 'overflow-hidden border-solid border-[var(--them-color)] border-2rpx rounded-26rpx h84rpx w-84rpx' : 'h72rpx w-72rpx']"
+              :class="[topNavActive == item.code ? 'overflow-hidden border-solid border-[var(--them-color)] border-2rpx rounded-26rpx h84rpx w-84rpx' : 'h72rpx w-72rpx']"
             />
-            <view class="mt16rpx text-22rpx" :class="[topNavActive == idx ? 'bg-[var(--them-color)] rounded-18rpx px-8rpx py2rpx text-white text-24rpx' : '']">
-              {{ item.title }}
+            <view
+              class="mt16rpx text-22rpx"
+              :class="[topNavActive == item.code ? 'bg-[var(--them-color)] rounded-18rpx px-8rpx py2rpx text-white text-24rpx' : '']"
+            >
+              {{ item.name }}
             </view>
           </view>
         </view>
       </scroll-view>
-      <view class="right-nav box-border h144rpx w64rpx flex flex-shrink-0 flex-col items-center justify-center px20rpx text-24rpx font-semibold" @click="handleOpen">
+      <view
+        class="right-nav box-border h144rpx w64rpx flex flex-shrink-0 flex-col items-center justify-center px20rpx text-24rpx font-semibold"
+        @click="handleOpen"
+      >
         展开
-        <image
-          :src="`${StaticUrl}/xia.png`"
-          class="mt20rpx h20rpx w20rpx"
-        />
+        <image :src="`${StaticUrl}/xia.png`" class="mt20rpx h20rpx w20rpx" />
       </view>
       <wd-popup v-model="show" position="top" custom-style="border-radius:32rpx;">
-        <view class="box-border bg-#F4FFD1 p24rpx" :style="{ paddingTop: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 12}px` }">
+        <view
+          class="box-border bg-#F4FFD1 p24rpx"
+          :style="{ paddingTop: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 12}px` }"
+        >
           <view class="mb24rpx mt24rpx text-28rpx font-semibold">
             全部一级分类
           </view>
           <view class="h400rpx overflow-y-scroll">
             <view class="grid grid-cols-5 box-border gap-x-24rpx gap-y-24rpx">
-              <view v-for="item, idx in classfiylist" :key="idx" class="box-border h150rpx w114rpx flex flex-col items-center justify-center" @click="handleTopNavChange(idx)">
+              <view
+                v-for="item in classfiylist" :key="item.code"
+                class="box-border h150rpx w114rpx flex flex-col items-center justify-center"
+                @click="handleTopNavChange(item)"
+              >
                 <image
                   :src="item.icon"
-                  :class="[topNavActive == idx ? 'overflow-hidden border-solid border-[var(--them-color)] border-2rpx rounded-26rpx h88rpx w-88rpx' : 'h76rpx w-76rpx']"
+                  :class="[topNavActive == item.code ? 'overflow-hidden border-solid border-[var(--them-color)] border-2rpx rounded-26rpx h88rpx w-88rpx' : 'h76rpx w-76rpx']"
                 />
-                <view class="mt16rpx whitespace-nowrap text-nowrap text-22rpx" :class="[topNavActive == idx ? 'bg-[var(--them-color)] rounded-18rpx px-8rpx py2rpx text-white text-24rpx' : '']">
-                  {{ item.title }}
+                <view
+                  class="mt16rpx whitespace-nowrap text-nowrap text-22rpx"
+                  :class="[topNavActive == item.code ? 'bg-[var(--them-color)] rounded-18rpx px-8rpx py2rpx text-white text-24rpx' : '']"
+                >
+                  {{ item.name }}
                 </view>
               </view>
             </view>
@@ -175,15 +227,16 @@ function onScroll(e) {
       </wd-popup>
     </view>
     <view class="wraper">
-      <wd-sidebar v-model="active" @change="handleChange">
-        <wd-sidebar-item v-for="(item, index) in categories" :key="index" :value="index" :label="item.label" />
+      <wd-sidebar v-model="leftActive" @change="handleChange">
+        <wd-sidebar-item
+          v-for="(item) in categories" :key="item.code"
+          custom-style="font-size:28rpx; white-space: nowrap;text-overflow: ellipsis;overflow: hidden;"
+          :value="item.code" :label="item.name"
+        />
       </wd-sidebar>
       <view class="content">
         <view class="p20rpx">
-          <image
-            :src="`${StaticUrl}/class.png`"
-            class="h144rpx w-full"
-          />
+          <image :src="`${StaticUrl}/class.png`" class="h144rpx w-full" />
           <view class="my20rpx flex items-center justify-end rounded-16rpx bg-#F6F6F6 px20rpx py8rpx">
             <view class="mr40rpx text-24rpx">
               销量
@@ -191,28 +244,33 @@ function onScroll(e) {
             <wd-sort-button v-model="sortClassBtn" :line="false" title="价格" />
           </view>
         </view>
-        <scroll-view :style="{ height: `calc(100vh - ${navHeight} - 600rpx)` }" scroll-y scroll-with-animation :scroll-top="scrollTop" :throttle="false" @scroll="onScroll">
-          <view class="p20rpx">
-            <view v-for="item in 20" :key="item">
+        <scroll-view
+          :lower-threshold="10"
+          :refresher-triggered="isTopLoading" :scroll-top="scrollTop"
+          :style="{ height: `calc(100vh - ${navHeight} - 700rpx)` }" enable-passive scroll-y scroll-with-animation scroll-anchoring refresher-enabled :throttle="false"
+          @refresherrefresh="handleSrollTop" @scrolltolower="handlScrollBottom"
+        >
+          <view v-if="productList.length" class="p20rpx">
+            <view v-for="item in productList" :key="item.id" @click="router.push({ name: 'xsb-goods', params: { id: String(item.id) } })">
               <view class="flex">
-                <view class="mr20rpx h172rpx w172rpx flex-shrink-0 rounded-16rpx bg-#F6F6F6" />
-                <view>
+                <view class="mr20rpx h172rpx w172rpx flex-shrink-0 overflow-hidden rounded-16rpx bg-#F6F6F6">
+                  <image :src="item.pic" lazy-load class="h-full w-full" />
+                </view>
+                <view class="flex-1">
                   <view class="text-left text-28rpx font-semibold">
-                    <view v-for="i in 2" :key="i" class="mr5px inline-block">
+                    <!-- <view v-for="i in 2" :key="i" class="mr5px inline-block">
                       <wd-tag type="primary">
                         新品{{ i }}
                       </wd-tag>
-                    </view>
-                    海湾高盐特大白虾200g
+                    </view> -->
+                    {{ item.prodName }}
                   </view>
                   <view class="text-22rpx text-#AAAAAA">
                     <view class="mt10rpx flex items-center">
-                      <view>一年一季</view>
-                      <wd-divider vertical />
-                      <view>新鲜翠绿</view>
+                      <view>{{ item.spec }}</view>
                     </view>
                     <view class="mt6rpx">
-                      已售3.6万+
+                      已售 {{ item.soldNum }}+
                     </view>
                   </view>
                   <view class="flex items-center justify-between">
@@ -220,14 +278,11 @@ function onScroll(e) {
                       <text class="text-24rpx">
                       </text> <text class="text-36rpx">
-                        9.99
+                        {{ item.channelProdPrice }}
                       </text>
                     </view>
-                    <view>
-                      <image
-                        :src="`${StaticUrl}/cart-yes.png`"
-                        class="h52rpx w52rpx"
-                      />
+                    <view @click.stop="handleAddCart($event, item)">
+                      <image :src="`${StaticUrl}/cart-yes.png`" class="h52rpx w52rpx" />
                     </view>
                   </view>
                 </view>
@@ -236,28 +291,88 @@ function onScroll(e) {
                 <wd-divider color="#F0F0F0" />
               </view>
             </view>
-            <view class="h60rpx" />
+            <wd-loadmore :state="goodsLoading" />
           </view>
+          <wd-status-tip v-else image="content" tip="暂无内容" />
         </scroll-view>
       </view>
     </view>
+    <view
+      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="cart-box h100rpx w100rpx" />
+        </view>
+        <view class="flex items-center">
+          <view class="flex items-center font-semibold">
+            <view class="text-22rpx text-#222">
+              总计:
+            </view>
+            <view class="flex items-baseline text-24rpx text-#FF4A39">
+              ¥
+              <text class="text-36rpx">
+                8.9
+              </text>
+            </view>
+          </view>
+          <view class="ml20rpx w160rpx">
+            <wd-button block size="large">
+              结算
+            </wd-button>
+          </view>
+        </view>
+      </view>
+    </view>
+    <view v-if="showball" class="ball">
+      <image :src="`${StaticUrl}/cart-yes.png`" class="cart-img h52rpx w52rpx" />
+    </view>
   </view>
 </template>
 
 <style scoped lang="scss">
-.right-nav{
-  background: linear-gradient( 180deg, #FAFFEC 0%, #F6FFDE 11%, #E7FFA5 100%);
-  box-shadow: -10rpx 0rpx 12rpx 2rpx rgba(0,0,0,0.07);
+.right-nav {
+  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);
 }
+
 .wraper {
   display: flex;
-  height: calc(100vh - var(--window-top) - v-bind(navHeight) - 290rpx);
-  height: calc(100vh - var(--window-top) - constant(safe-area-inset-bottom) - v-bind(navHeight) - 290rpx);
-  height: calc(100vh - var(--window-top) - env(safe-area-inset-bottom) - v-bind(navHeight) - 290rpx);
+  height: calc(100vh - var(--window-top) - v-bind(navHeight) - 390rpx);
+  height: calc(100vh - var(--window-top) - constant(safe-area-inset-bottom) - v-bind(navHeight) - 390rpx);
+  height: calc(100vh - var(--window-top) - env(safe-area-inset-bottom) - v-bind(navHeight) - 390rpx);
 }
 
 .content {
   flex: 1;
   background: #fff;
+
+}
+
+@keyframes moveX {
+  to {
+    transform: translateX(v-bind(x));
+  }
+}
+
+@keyframes moveY {
+  to {
+    transform: translateY(v-bind(y));
+  }
+}
+
+.ball {
+  position: fixed;
+  z-index: 80;
+  left: v-bind(left);
+  top: v-bind(top);
+  animation: moveX .5s linear forwards;
+}
+
+.cart-img {
+  animation: moveY .5s cubic-bezier(1,-1.26,1,1) forwards;
 }
 </style>

+ 50 - 58
src/subPack-xsb/commonTab/components/index.vue

@@ -2,11 +2,15 @@
 import { StaticUrl, VITE_OSS_BASE_URL } from '@/config'
 import router from '@/router'
 
+const props = defineProps<{ categoryList: Api.xsbCategories[] }>()
+
+const emit = defineEmits(['changeNav'])
+
 const { ScrollDown } = storeToRefs(useSysXsbStore())
 
 const { statusBarHeight, MenuButtonHeight } = storeToRefs(useSysStore())
 const { name } = storeToRefs(useAddressStore())
-
+const { topNavActive, leftActive } = storeToRefs(useSysXsbStore())
 const current = ref(0)
 const swiperList = ref([
   'https://wot-ui.cn/assets/redpanda.jpg',
@@ -16,28 +20,8 @@ const swiperList = ref([
   'https://wot-ui.cn/assets/meng.jpg',
 ])
 const navActive = ref(0)
-const classfiylist = ref([
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/shu.png` },
-])
-const swiperClassList = ref([
-  { title: '露营季', icon: `${StaticUrl}/zhi.png` },
-  { title: '方便菜', icon: `${StaticUrl}/zhi.png` },
-  { title: '日用清洁', icon: `${StaticUrl}/zhi.png` },
-  { title: '个护美妆', icon: `${StaticUrl}/zhi.png` },
-  { title: '轻食沙拉', icon: `${StaticUrl}/zhi.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/zhi.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/zhi.png` },
-  { title: '蔬菜豆品', icon: `${StaticUrl}/zhi.png` },
-])
+const classfiylist = computed(() => props.categoryList.slice(0, 10))
+const swiperClassList = computed(() => props.categoryList.slice(10, props.categoryList.length))
 const navList = ref([
   { title: '为你推荐' },
   { title: '疯狂折扣' },
@@ -65,6 +49,14 @@ function handleScroll(e: UniHelper.ScrollViewOnScrollEvent) {
 function handleCommonName(name: string) {
   router.push({ name })
 }
+function handleCommonClass(item: Api.xsbCategories) {
+  topNavActive.value = item.code
+  leftActive.value = item.children[0].code
+  emit('changeNav')
+}
+function handleSwiperClick(e: { index: number, item: Api.xsbCategories }) {
+  handleCommonClass(e.item)
+}
 </script>
 
 <template>
@@ -95,7 +87,10 @@ function handleCommonName(name: string) {
           <view class="flex items-center pb14rpx pl24rpx pt16rpx">
             <wd-icon name="search" size="14" color="#ccc" />
             <view class="search ml12rpx h-full w-full overflow-hidden">
-              <wd-notice-bar :text="['霸王茶姬', '牛奶', '洗衣机']" custom-class="notice-bar" color="#ccc" background-color="#fff" direction="vertical" />
+              <wd-notice-bar
+                :text="['霸王茶姬', '牛奶', '洗衣机']" custom-class="notice-bar" color="#ccc"
+                background-color="#fff" direction="vertical"
+              />
             </view>
           </view>
           <view
@@ -106,7 +101,10 @@ function handleCommonName(name: string) {
         </view>
         <scroll-view scroll-x class="mb20rpx mt20rpx h44rpx whitespace-nowrap">
           <view class="h-full flex items-center">
-            <view v-for="item in 10" :key="item" class="mr32rpx h-full flex items-center justify-center rounded-22rpx bg-[rgba(255,255,255,.5)] px16rpx text-24rpx">
+            <view
+              v-for="item in 10" :key="item"
+              class="mr32rpx h-full flex items-center justify-center rounded-22rpx bg-[rgba(255,255,255,.5)] px16rpx text-24rpx"
+            >
               {{ item }}苹果
             </view>
           </view>
@@ -118,29 +116,21 @@ function handleCommonName(name: string) {
           <wd-card title="" custom-class="card-zt">
             <view class="px24rpx pt24rpx">
               <view class="grid grid-cols-5 gap-x-38rpx gap-y-24rpx">
-                <view v-for="item, index in classfiylist" :key="index">
-                  <image
-                    :src="item.icon"
-                    class="h100rpx w100rpx"
-                  />
+                <view v-for="item, index in classfiylist" :key="index" @click="handleCommonClass(item)">
+                  <image :src="item.icon" class="h100rpx w100rpx" />
                   <view class="mt12rpx text-24rpx text-#222">
-                    {{ item.title }}
+                    {{ item.name }}
                   </view>
                 </view>
               </view>
             </view>
             <view class="swiper-img swiper pb20rpx">
               <wd-swiper
-                :list="swiperClassList"
-                :indicator="{ type: 'dots-bar' }"
-                height="80"
-                :display-multiple-items="5"
-                :autoplay="false"
-                :loop="false"
-                custom-indicator-class="custom-indicator-class"
-                value-key="icon"
-                text-key="title"
+                :list="swiperClassList" :indicator="{ type: 'dots-bar' }" height="80"
+                :display-multiple-items="5" :autoplay="false" :loop="false"
+                custom-indicator-class="custom-indicator-class" value-key="icon" text-key="name"
                 image-mode="aspectFit"
+                @click="handleSwiperClick"
               />
             </view>
           </wd-card>
@@ -155,23 +145,22 @@ function handleCommonName(name: string) {
           </wd-notice-bar>
         </view>
         <view class="mt20rpx">
-          <image
-            :src="`${VITE_OSS_BASE_URL}2025/12/6671332ceef64985858aa8b548027bd3.png`"
-            class="h236rpx w-full"
-          />
+          <image :src="`${VITE_OSS_BASE_URL}2025/12/6671332ceef64985858aa8b548027bd3.png`" class="h236rpx w-full" />
         </view>
         <scroll-view scroll-x class="mb20rpx mt20rpx whitespace-nowrap">
           <view class="flex items-center">
             <view v-for="item, idx in navList" :key="idx" class="relative mr44rpx" @click="handleChangeNav(idx)">
-              <image
-                v-show="idx == 2"
-                :src="`${StaticUrl}/chaozhi.png`"
-                class="relative z-2 h-29.06rpx w-105.34rpx"
-              />
-              <text v-show="idx != 2" class="relative z-2 text-32rpx" :class="[navActive == idx ? 'text-36rpx font-semibold' : '']">
+              <image v-show="idx == 2" :src="`${StaticUrl}/chaozhi.png`" class="relative z-2 h-29.06rpx w-105.34rpx" />
+              <text
+                v-show="idx != 2" class="relative z-2 text-32rpx"
+                :class="[navActive == idx ? 'text-36rpx font-semibold' : '']"
+              >
                 {{ item.title }}
               </text>
-              <view v-show="navActive == idx" class="nav-line absolute bottom-0rpx left-50% z-1 h18rpx w80rpx rounded-10rpx -translate-x-50%" />
+              <view
+                v-show="navActive == idx"
+                class="nav-line absolute bottom-0rpx left-50% z-1 h18rpx w80rpx rounded-10rpx -translate-x-50%"
+              />
             </view>
           </view>
         </scroll-view>
@@ -180,9 +169,15 @@ function handleCommonName(name: string) {
             <scroll-view scroll-y type="custom">
               <grid-view type="masonry" cross-axis-count="2" main-axis-gap="10" cross-axis-gap="10">
                 <wd-swiper v-model:current="current" :autoplay="false" :list="swiperList" :height="231" />
-                <view v-for="it in 20" :key="it" class="overflow-hidden rounded-16rpx bg-white pb16rpx" @click="handleCommonName('xsb-goods')">
+                <view
+                  v-for="it in 20" :key="it" class="overflow-hidden rounded-16rpx bg-white pb16rpx"
+                  @click="handleCommonName('xsb-goods')"
+                >
                   <view class="relative h344rpx">
-                    <image :src="`${VITE_OSS_BASE_URL}2025/11/9d42892888304abf85487deea0271f62.png`" class="h344rpx w344rpx" />
+                    <image
+                      :src="`${VITE_OSS_BASE_URL}2025/11/9d42892888304abf85487deea0271f62.png`"
+                      class="h344rpx w344rpx"
+                    />
                   </view>
                   <view class="mt20rpx px20rpx">
                     <view class="text-left text-28rpx font-semibold">
@@ -208,10 +203,7 @@ function handleCommonName(name: string) {
                           </view>
                         </view>
                       </view>
-                      <image
-                        :src="`${StaticUrl}/cart-yes.png`"
-                        class="h52rpx w52rpx"
-                      />
+                      <image :src="`${StaticUrl}/cart-yes.png`" class="h52rpx w52rpx" />
                     </view>
                   </view>
                 </view>
@@ -226,7 +218,7 @@ function handleCommonName(name: string) {
 </template>
 
 <style scoped lang="scss">
-.content{
+.content {
   height: calc(100vh - var(--window-top) - 80rpx);
 }
 </style>

+ 0 - 26
src/subPack-xsb/commonTab/components/my.vue

@@ -101,30 +101,4 @@ const tabList = ref([
 .header {
  background: linear-gradient( 113deg, #F7FFDC 0%, #E0FF8E 25%, #F2FFCE 51%, #E3FF98 83%, #F6FFD6 100%);
 }
-
-.page-class {
-  :deep() {
-    .login-btn {
-      min-width: 0 !important;
-      width: 180rpx;
-    }
-
-    .cell-title {
-      padding-left: 20rpx;
-    }
-  }
-
-}
-
-.item-cell {
-  :deep() {
-    .card {
-      padding: 0 !important;
-    }
-    .cell-group{
-      overflow: hidden;
-      border-radius: 12rpx;
-    }
-  }
-}
 </style>

+ 42 - 5
src/subPack-xsb/commonTab/index.vue

@@ -22,7 +22,8 @@ definePage({
     disableScroll: true,
   },
 })
-const { ScrollDown } = storeToRefs(useSysXsbStore())
+const { ScrollDown, topNavActive, leftActive } = storeToRefs(useSysXsbStore())
+const commonCategoryData = ref<Api.xsbCategories[]>([])
 const tabbarItems = ref([
   { name: 'xsb-home', value: null, active: true, title: '首页', icon1: '', icon2: '' },
   { name: 'xsb-classfiy', value: null, active: false, title: '分类', icon1: class1, icon2: class2 },
@@ -53,17 +54,30 @@ function handleClick() {
 onLoad(() => {
   ScrollDown.value = false
 })
+async function getCategories() {
+  const res = await Apis.xsb.categories({ data: { shopId: 1, channelId: 1 } })
+  commonCategoryData.value = res
+  topNavActive.value = res[0].code
+  leftActive.value = res[0].children[0].code
+}
+onMounted(() => {
+  getCategories()
+})
 </script>
 
 <template>
   <view class="page-xsb">
-    <indexHome v-if="tabbarName == 'xsb-home'" />
+    <indexHome
+      v-if="tabbarName == 'xsb-home'" :category-list="commonCategoryData"
+      @change-nav="tabbarName = 'xsb-classfiy'"
+    />
     <cart v-if="tabbarName == 'xsb-cart'" />
-    <classfiy v-if="tabbarName == 'xsb-classfiy'" />
+    <classfiy v-if="tabbarName == 'xsb-classfiy'" :category-list="commonCategoryData" />
     <my v-if="tabbarName == 'xsb-my'" />
     <wd-tabbar
       :model-value="tabbarName" safe-area-inset-bottom placeholder fixed :bordered="false"
-      custom-class="custom-tab" :custom-style="`box-shadow:${tabbarName == 'xsb-cart' ? '' : ' 0rpx -6rpx 12rpx 2rpx rgba(0, 0, 0, 0.09)'}`"
+      custom-class="custom-tab"
+      :custom-style="`box-shadow:${tabbarName == 'xsb-cart' || tabbarName == 'xsb-classfiy' ? '' : ' 0rpx -6rpx 12rpx 2rpx rgba(0, 0, 0, 0.09)'}`"
       @change="handleTabbarChange"
     >
       <wd-tabbar-item
@@ -89,6 +103,29 @@ onLoad(() => {
   </view>
 </template>
 
-<style scoped>
+<style scoped lang="scss">
+.page-xsb {
+  :deep() {
+    .login-btn {
+      min-width: 0 !important;
+      width: 180rpx;
+    }
+
+    .cell-title {
+      padding-left: 20rpx;
+    }
 
+    .item-cell {
+      .card {
+        padding: 0 !important;
+      }
+
+      .cell-group {
+        overflow: hidden;
+        border-radius: 12rpx;
+      }
+    }
+  }
+
+}
 </style>

+ 70 - 51
src/subPack-xsb/goods/index.vue

@@ -11,13 +11,13 @@ definePage({
     navigationBarTitleText: '星闪豹商品详情',
   },
 })
-const goodsTab = ref(0)
+// const goodsTab = ref(0)
 const current = ref<number>(0)
 const currentDetaile = ref(0)
-const goodsTabList = ref([
-  { title: '相似商品', id: 0 },
-  { title: '经常一起买', id: 1 },
-])
+// const goodsTabList = ref([
+//   { title: '相似商品', id: 0 },
+//   { title: '经常一起买', id: 1 },
+// ])
 const goodsDetaileTabList = ref([
   { title: '商品', id: 0 },
   { title: '评价', id: 1 },
@@ -26,31 +26,46 @@ const goodsDetaileTabList = ref([
 ])
 const goodsTabData = ref([])
 const isShowTab = ref(false)
-const currentTabGoods = ref(0)
+// const currentTabGoods = ref(0)
+const goodsId = ref()
+onLoad((option: any) => {
+  goodsId.value = option.id
+  getGoodsDetaile()
+})
 
-const swiperList = ref([
-  'https://wot-ui.cn/assets/redpanda.jpg',
-  'https://wot-ui.cn/assets/capybara.jpg',
-  'https://wot-ui.cn/assets/panda.jpg',
-  'https://wot-ui.cn/assets/moon.jpg',
-  'https://wot-ui.cn/assets/meng.jpg',
-])
+const goodsInfo = ref<Api.xsbProductDetail>()
+
+const swiperList = computed(() => {
+  if (goodsInfo.value) {
+    return goodsInfo.value.imgs?.split(',')
+  }
+  return []
+})
+const goodsContent = computed(() => {
+  if (goodsInfo.value?.content) {
+    return goodsInfo.value.content.replace(
+      /<img/gi,
+      '<img style="max-width:100%;height:auto" ',
+    )
+  }
+  return '暂无数据'
+})
 onMounted(() => {
-  goodsTabData.value = splitArrayToPages([{ id: 1, name: '商品' }, { id: 2, name: '商品' }, { id: 3, name: '商品' }, { id: 4, name: '商品' }, { id: 8, name: '商品' }, { id: 5, name: '商品' }, { id: 10, name: '商品' }, { id: 6, name: '商品' }, { id: 9, name: '商品' }, { id: 7, name: '商品' }, { id: 11, name: '商品' }])
+  // goodsTabData.value = splitArrayToPages([{ id: 1, name: '商品' }, { id: 2, name: '商品' }, { id: 3, name: '商品' }, { id: 4, name: '商品' }, { id: 8, name: '商品' }, { id: 5, name: '商品' }, { id: 10, name: '商品' }, { id: 6, name: '商品' }, { id: 9, name: '商品' }, { id: 7, name: '商品' }, { id: 11, name: '商品' }])
 
   console.log(goodsTabData.value, 'goodsTabData')
 })
-function splitArrayToPages<T>(arr: T[]): T[][] {
-  const PAGE_SIZE = 6
-  const MAX_PAGES = 3
-  const result: T[][] = []
-  for (let i = 0; i < Math.min(MAX_PAGES, Math.ceil(arr.length / PAGE_SIZE)); i++) {
-    const start = i * PAGE_SIZE
-    const end = start + PAGE_SIZE
-    result.push(arr.slice(start, end))
-  }
-  return result
-}
+// function splitArrayToPages<T>(arr: T[]): T[][] {
+//   const PAGE_SIZE = 6
+//   const MAX_PAGES = 3
+//   const result: T[][] = []
+//   for (let i = 0; i < Math.min(MAX_PAGES, Math.ceil(arr.length / PAGE_SIZE)); i++) {
+//     const start = i * PAGE_SIZE
+//     const end = start + PAGE_SIZE
+//     result.push(arr.slice(start, end))
+//   }
+//   return result
+// }
 onPageScroll((e) => {
   if (e.scrollTop >= 315) {
     isShowTab.value = true
@@ -63,10 +78,16 @@ function handleGoCurren(id: number) {
   currentDetaile.value = id
   uni.pageScrollTo({ selector: `.view-${id}` })
 }
+
+async function getGoodsDetaile() {
+  const res = await Apis.xsb.getProductDetail({ data: { id: goodsId.value } })
+  console.log(res, '请求')
+  goodsInfo.value = res
+}
 </script>
 
 <template>
-  <view class="page-xsb">
+  <view v-if="goodsInfo" class="page-xsb">
     <wd-navbar
       title="商品详情" :custom-style="`background-color:${isShowTab ? '#FFF !important' : 'transparent !important'}`" :bordered="false" :z-index="99"
       safe-area-inset-top left-arrow fixed @click-left="router.back()"
@@ -85,12 +106,12 @@ function handleGoCurren(id: number) {
           </view>
           <view class="text-36rpx line-height-[36rpx]">
-            1.395
+            {{ goodsInfo?.channelProdPrice }}
           </view>
         </view>
-        <view>
+        <!-- <view>
           <image class="h40rpx w40rpx" :src="`${StaticUrl}/collect-yes.png`" />
-        </view>
+        </view> -->
       </view>
     </view>
     <view class="sticky left-0 z-99 box-border h84rpx w-full flex items-center justify-between bg-white px24rpx" :style="{ top: `${statusBarHeight + MenuButtonHeight}px`, opacity: isShowTab ? 1 : 0 }" :class="[isShowTab ? '' : '']">
@@ -102,31 +123,31 @@ function handleGoCurren(id: number) {
     <view class="px24rpx">
       <view class="-mt64rpx">
         <view class="text-left text-28rpx font-semibold">
-          <view v-for="i in 2" :key="i" class="mr5px inline-block">
+          <!-- <view v-for="i in 2" :key="i" class="mr5px inline-block">
             <wd-tag type="primary">
               新品{{ i }}
             </wd-tag>
-          </view>
-          大荔冬枣大果250g
+          </view> -->
+          {{ goodsInfo.prodName }}
         </view>
       </view>
-      <view class="mt16rpx text-24rpx text-#AAAAAA">
+      <!-- <view class="mt16rpx text-24rpx text-#AAAAAA">
         优选产地,皮薄核小,维c满满
-      </view>
-      <view class="mt20rpx flex items-center">
+      </view> -->
+      <!-- <view class="mt20rpx flex items-center">
         <view
           v-for="item in 2" :key="item"
           class="mr16rpx flex items-center justify-center border-1rpx border-#BC9264 rounded-8rpx border-solid bg-[#FFF5E9] px16rpx py6rpx text-24rpx text-#BC9264 font-semibold"
         >
           一口化渣{{ item }}
         </view>
-      </view>
+      </view> -->
 
       <view class="mt20rpx flex items-center justify-between rounded-16rpx bg-white p24rpx">
         <view class="flex items-center">
           <view class="flex-shrink-0">
             <view class="text-28rpx font-semibold">
-              大荔冬枣
+              {{ goodsInfo.brandName }}
             </view>
             <view class="mt16rpx text-24rpx text-#AAAAAA">
               品牌
@@ -139,10 +160,10 @@ function handleGoCurren(id: number) {
         <view class="flex items-center">
           <view class="flex-shrink-0">
             <view class="text-28rpx font-semibold">
-              大荔冬枣
+              {{ goodsInfo.weight }}
             </view>
             <view class="mt16rpx text-24rpx text-#AAAAAA">
-              产地(以实际商品批次为准)
+              重量
             </view>
           </view>
           <view class="mx24rpx">
@@ -152,16 +173,16 @@ function handleGoCurren(id: number) {
         <view class="flex items-center">
           <view class="flex-shrink-0">
             <view class="text-28rpx font-semibold">
-              30天
+              {{ goodsInfo.weightUnit }}
             </view>
             <view class="mt16rpx text-24rpx text-#AAAAAA">
-              保质期
+              单位
             </view>
           </view>
         </view>
         <wd-icon name="chevron-right" size="22px" color="#AAAAAA" />
       </view>
-      <view class="view-1 mt20rpx flex items-center rounded-16rpx bg-white p24rpx">
+      <!-- <view class="view-1 mt20rpx flex items-center rounded-16rpx bg-white p24rpx">
         <view class="w-full flex items-center justify-between">
           <view class="text-32rpx font-semibold">
             评价(10万+)
@@ -176,9 +197,9 @@ function handleGoCurren(id: number) {
             <wd-icon name="chevron-right" size="22px" color="#AAAAAA" />
           </view>
         </view>
-      </view>
+      </view> -->
 
-      <view class="mt20rpx rounded-16rpx bg-white p24rpx">
+      <!-- <view class="mt20rpx rounded-16rpx bg-white p24rpx">
         <wd-tabs v-model="goodsTab">
           <block v-for="item in goodsTabList" :key="item.id">
             <wd-tab :title="item.title" />
@@ -217,18 +238,16 @@ function handleGoCurren(id: number) {
         <view class="mt24rpx flex items-center justify-center">
           <view v-for="item, idx in goodsTabData" :key="idx" class="mr14rpx transition-all transition-duration-400 ease-in" :class="[currentTabGoods == idx ? 'rounded-12rpx w-40rpx h12rpx bg-[var(--them-color)]' : 'w12rpx h12rpx rounded-50% bg-#F0F0F0']" />
         </view>
-      </view>
+      </view> -->
     </view>
 
     <view class="view-2 mt20rpx bg-white">
       <view class="p24rpx text-32rpx font-semibold">
         商品详情
-      </view>
-      <view class="h-200rpx">
-        图片
+        <rich-text :nodes="goodsContent" />
       </view>
     </view>
-    <view class="view-3 mt28rpx flex items-center justify-center">
+    <!-- <view class="view-3 mt28rpx flex items-center justify-center">
       <view class="flex items-center">
         <image
           :src="`${StaticUrl}/goods-recommend.png`"
@@ -242,7 +261,7 @@ function handleGoCurren(id: number) {
           class="ml12rpx h25rpx w26rpx"
         />
       </view>
-    </view>
+    </view> -->
     <view class="h180rpx" />
     <view class="ios shadow-fixed fixed bottom-0 left-0 w-full rounded-t-32rpx bg-white">
       <view class="flex items-center justify-between px24rpx py20rpx">
@@ -275,7 +294,7 @@ function handleGoCurren(id: number) {
         </view>
         <view class="flex items-center">
           <view class="w220rpx">
-            <wd-button plain hairline block>
+            <wd-button hairline plain block>
               加入购物车
             </wd-button>
           </view>

+ 13 - 0
src/subPack-xsb/store-xsb/cart.ts

@@ -0,0 +1,13 @@
+import { defineStore } from 'pinia'
+
+interface cartState {
+  cartList: Api.xsbCategoryProductList[]
+}
+export const useXsbCartStore = defineStore('xsb-cart', {
+  state: (): cartState => ({
+    cartList: [],
+  }),
+  actions: {
+
+  },
+})

+ 10 - 0
src/subPack-xsb/store-xsb/sys.ts

@@ -2,10 +2,20 @@ import { defineStore } from 'pinia'
 
 interface SysState {
   ScrollDown: boolean
+  /**
+   * 分类顶部激活id
+   */
+  topNavActive: string
+  /**
+   * 二级分类激活id
+   */
+  leftActive: string
 }
 export const useSysXsbStore = defineStore('system-xsb', {
   state: (): SysState => ({
     ScrollDown: false,
+    topNavActive: '',
+    leftActive: '',
   }),
   actions: {
     getTabbarItemValue(name: string) {