Преглед изворни кода

feature(uniapp分类): 分类列表以及商品加载

luowenfeng пре 2 година
родитељ
комит
e2a6530acb
16 измењених фајлова са 585 додато и 139 уклоњено
  1. 40 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/category/AppCategoryController.java
  2. 38 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/category/vo/AppCategoryListRespVO.java
  3. 37 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java
  4. 18 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageReqVO.java
  5. 52 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageRespVO.java
  6. 2 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/category/CategoryConvert.java
  7. 7 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java
  8. 8 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/CategoryService.java
  9. 11 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/CategoryServiceImpl.java
  10. 11 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java
  11. 15 1
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java
  12. 5 0
      yudao-ui-app/api/category.js
  13. 5 0
      yudao-ui-app/api/product.js
  14. 10 2
      yudao-ui-app/pages.json
  15. 165 136
      yudao-ui-app/pages/category/category.vue
  16. 161 0
      yudao-ui-app/pages/category/product-list.vue

+ 40 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/category/AppCategoryController.java

@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.module.product.controller.app.category;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.product.controller.app.category.vo.AppCategoryListRespVO;
+import cn.iocoder.yudao.module.product.convert.category.CategoryConvert;
+import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO;
+import cn.iocoder.yudao.module.product.service.category.CategoryService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.Comparator;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Api(tags = "用户 APP - 商品分类")
+@RestController
+@RequestMapping("/product/category")
+@Validated
+public class AppCategoryController {
+
+    @Resource
+    private CategoryService categoryService;
+
+    @GetMapping("/list")
+    @ApiOperation("获得商品分类列表")
+    public CommonResult<List<AppCategoryListRespVO>> listByQuery() {
+        List<CategoryDO> list = categoryService.getCategoryList();
+        list.sort(Comparator.comparing(CategoryDO::getSort));
+        return success(CategoryConvert.INSTANCE.convertList03(list));
+    }
+
+
+
+}

+ 38 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/category/vo/AppCategoryListRespVO.java

@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.module.product.controller.app.category.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+@Data
+@ApiModel(value = "App - 商品分类 列表 Request VO", description = "参数和 CategoryBaseVO 是一致的")
+public class AppCategoryListRespVO {
+
+    @ApiModelProperty(value = "分类编号", required = true, example = "2")
+    private Long id;
+
+    @ApiModelProperty(value = "父分类编号", required = true, example = "1")
+    @NotNull(message = "父分类编号不能为空")
+    private Long parentId;
+
+    @ApiModelProperty(value = "分类名称", required = true, example = "办公文具")
+    @NotBlank(message = "分类名称不能为空")
+    private String name;
+
+    @ApiModelProperty(value = "分类图标")
+    @NotBlank(message = "分类图标不能为空")
+    private String icon;
+
+    @ApiModelProperty(value = "分类图片", required = true)
+    @NotBlank(message = "分类图片不能为空")
+    private String bannerUrl;
+
+    @ApiModelProperty(value = "分类排序", required = true, example = "1")
+    private Integer sort;
+
+    @ApiModelProperty(value = "分类描述", required = true, example = "描述")
+    private String description;
+}

+ 37 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java

@@ -0,0 +1,37 @@
+package cn.iocoder.yudao.module.product.controller.app.spu;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.product.controller.admin.spu.vo.SpuPageReqVO;
+import cn.iocoder.yudao.module.product.controller.admin.spu.vo.SpuRespVO;
+import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO;
+import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO;
+import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Api(tags = "用户 APP -  商品spu")
+@RestController
+@RequestMapping("/product/spu")
+@Validated
+public class AppProductSpuController {
+
+    @Resource
+    private ProductSpuService spuService;
+
+    @GetMapping("/page")
+    @ApiOperation("获得商品spu分页")
+    public CommonResult<PageResult<AppSpuPageRespVO>> getSpuPage(@Valid AppSpuPageReqVO pageVO) {
+        return success(spuService.getSpuPage(pageVO));
+    }
+}

+ 18 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageReqVO.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.product.controller.app.spu.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@ApiModel("App - 商品spu分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class AppSpuPageReqVO extends PageParam {
+
+    @ApiModelProperty(value = "分类id")
+    private Long categoryId;
+}

+ 52 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageRespVO.java

@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.module.product.controller.app.spu.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+@ApiModel("App - 商品spu分页 Request VO")
+@Data
+public class AppSpuPageRespVO  {
+
+    @ApiModelProperty(value = "主键", required = true, example = "1")
+    private Long id;
+
+    @ApiModelProperty(value = "商品名称")
+    private String name;
+
+    @ApiModelProperty(value = "卖点", required = true)
+    @NotNull(message = "卖点不能为空")
+    private String sellPoint;
+
+    @ApiModelProperty(value = "描述", required = true)
+    @NotNull(message = "描述不能为空")
+    private String description;
+
+    @ApiModelProperty(value = "分类id", required = true)
+    @NotNull(message = "分类id不能为空")
+    private Long categoryId;
+
+    @ApiModelProperty(value = "商品主图地址,* 数组,以逗号分隔,最多上传15张", required = true)
+    @NotNull(message = "商品主图地址,* 数组,以逗号分隔,最多上传15张不能为空")
+    private List<String> picUrls;
+
+    @ApiModelProperty(value = "排序字段", required = true)
+    @NotNull(message = "排序字段不能为空")
+    private Integer sort;
+
+    @ApiModelProperty(value = "点赞初始人数")
+    private Integer likeCount;
+
+    @ApiModelProperty(value = "价格 单位使用:分")
+    private Integer price;
+
+    @ApiModelProperty(value = "库存数量")
+    private Integer quantity;
+
+    @ApiModelProperty(value = "上下架状态: 0 上架(开启) 1 下架(禁用)")
+    private Boolean status;
+
+}

+ 2 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/category/CategoryConvert.java

@@ -4,6 +4,7 @@ import java.util.*;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 
+import cn.iocoder.yudao.module.product.controller.app.category.vo.AppCategoryListRespVO;
 import org.mapstruct.Mapper;
 import org.mapstruct.factory.Mappers;
 import cn.iocoder.yudao.module.product.controller.admin.category.vo.*;
@@ -31,4 +32,5 @@ public interface CategoryConvert {
 
     List<CategoryExcelVO> convertList02(List<CategoryDO> list);
 
+    List<AppCategoryListRespVO> convertList03(List<CategoryDO> list);
 }

+ 7 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java

@@ -4,6 +4,8 @@ import java.util.*;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 
+import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO;
+import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO;
 import org.mapstruct.Mapper;
 import org.mapstruct.Mapping;
 import org.mapstruct.Named;
@@ -40,6 +42,11 @@ public interface ProductSpuConvert {
 
     List<SpuExcelVO> convertList02(List<ProductSpuDO> list);
 
+    SpuPageReqVO convert(AppSpuPageReqVO bean);
+
+    @Mapping(source = "picUrls", target = "picUrls", qualifiedByName = "tokenizeToStringArray")
+    AppSpuPageRespVO convertAppResp(ProductSpuDO list);
+
     @Named("tokenizeToStringArray")
     default List<String> translatePicUrlsArrayFromString(String picUrls) {
         return Arrays.asList(StringUtils.tokenizeToStringArray(picUrls, ","));

+ 8 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/CategoryService.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.product.service.category;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.product.controller.admin.category.vo.*;
+import cn.iocoder.yudao.module.product.controller.app.category.vo.AppCategoryListRespVO;
 import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO;
 
 import javax.validation.Valid;
@@ -83,4 +84,11 @@ public interface CategoryService {
      * @param categoryId 分类id
      */
     void validatedCategoryById(Long categoryId);
+
+    /**
+     * app端获得商品分类列表
+     *
+     * @return 商品分类列表
+     */
+    List<CategoryDO> getCategoryList();
 }

+ 11 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/CategoryServiceImpl.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.product.service.category;
 
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.exception.ErrorCode;
 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -13,6 +14,7 @@ import org.springframework.validation.annotation.Validated;
 import javax.annotation.Resource;
 import java.util.Collection;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;
@@ -106,4 +108,13 @@ public class CategoryServiceImpl implements CategoryService {
         return categoryMapper.selectList(treeListReqVO);
     }
 
+    @Override
+    public List<CategoryDO> getCategoryList() {
+        return categoryMapper.selectList()
+                .stream()
+                .filter(v->v.getStatus().equals(CommonStatusEnum.ENABLE.getStatus()))
+                .collect(Collectors.toList());
+    }
+
+
 }

+ 11 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java

@@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.product.service.spu;
 import java.util.*;
 import javax.validation.*;
 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
+import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO;
+import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO;
 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 
@@ -67,4 +69,13 @@ public interface ProductSpuService {
      */
     List<ProductSpuDO> getSpuList(SpuExportReqVO exportReqVO);
 
+    /**
+     * 获得商品spu分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 商品spu分页
+     */
+    PageResult<AppSpuPageRespVO> getSpuPage(AppSpuPageReqVO pageReqVO);
+
+
 }

+ 15 - 1
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java

@@ -8,9 +8,10 @@ import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO;
 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateReqVO;
 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO;
 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
+import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO;
+import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO;
 import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
 import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
-import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO;
 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
 import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper;
@@ -175,4 +176,17 @@ public class ProductSpuServiceImpl implements ProductSpuService {
         return ProductSpuMapper.selectList(exportReqVO);
     }
 
+    @Override
+    public PageResult<AppSpuPageRespVO> getSpuPage(AppSpuPageReqVO pageReqVO) {
+        PageResult<ProductSpuDO> productSpuDOPageResult = ProductSpuMapper.selectPage(ProductSpuConvert.INSTANCE.convert(pageReqVO));
+        PageResult<AppSpuPageRespVO> pageResult = new PageResult<>();
+        List<AppSpuPageRespVO> collect = productSpuDOPageResult.getList()
+                .stream()
+                .map(ProductSpuConvert.INSTANCE::convertAppResp)
+                .collect(Collectors.toList());
+        pageResult.setList(collect);
+        pageResult.setTotal(productSpuDOPageResult.getTotal());
+        return pageResult;
+    }
+
 }

+ 5 - 0
yudao-ui-app/api/category.js

@@ -0,0 +1,5 @@
+//请求工具参考https://ext.dcloud.net.cn/plugin?id=392
+const { http } = uni.$u
+
+// 查询分类列表
+export const categoryListData = params => http.get('product/category/list', { params })

+ 5 - 0
yudao-ui-app/api/product.js

@@ -0,0 +1,5 @@
+//请求工具参考https://ext.dcloud.net.cn/plugin?id=392
+const { http } = uni.$u
+
+// 查询商品spu列表
+export const productSpuPage = params => http.get('/product/spu/page', { params })

+ 10 - 2
yudao-ui-app/pages.json

@@ -12,6 +12,14 @@
 				"navigationBarTitleText": "分类"
 			}
 		},
+    {
+    	"path": "pages/category/product-list",
+    	"style": {
+        "navigationBarTitleText": "",
+        "navigationStyle": "custom",
+        "navigationBarTextStyle": "white"
+    	}
+    },
 		{
 			"path": "pages/cart/cart",
 			"style": {
@@ -112,7 +120,7 @@
 	"globalStyle": {
 		"navigationBarTextStyle": "black",
 		"navigationBarTitleText": "yudao-ui-app",
-		"navigationBarBackgroundColor": "#F8F8F8",
-		"backgroundColor": "#FFFFFF"
+		"navigationBarBackgroundColor": "#ffffff",
+		"backgroundColor": "#ffffff"
 	}
 }

+ 165 - 136
yudao-ui-app/pages/category/category.vue

@@ -1,176 +1,205 @@
 <template>
   <view class="container">
+    <!-- 搜索框 -->
     <view class="search-wrap">
-      <u-search placeholder="搜索" disabled height="32" :show-action="false" @click="handleSearchClick"></u-search>
+      <u-search placeholder="搜索" disabled height="32" bgColor="#f2f2f2" margin="0 20rpx" :show-action="false"
+        @click="handleSearchClick"></u-search>
     </view>
+
+    <!-- 分类内容 -->
     <view class="category-box">
-      <view class="box-left">
-        <view>
-          <view class="category-item" v-for="(item, index) in categoryList" :key="item.id">
-            <view class="item-title" :class="{ active: currentIndex === index }" @click="handleCategoryClick(index)">
-              <text>{{ item.name }}</text>
-            </view>
+      <!-- 左侧导航栏 -->
+      <scroll-view scroll-y="true" class='box-left'>
+        <view class="category-item" v-for="(item, index) in categoryList" :key="item.id">
+          <view class="item-title" :class="{ active: currentIndex === index }" @click="handleCategoryClick(index)">
+            <text>{{ item.name }}</text>
           </view>
         </view>
-      </view>
-      <view class="box-right">
-        <image class="category-image" :showLoading="true" :src="categoryList[currentIndex].image" width="530rpx" height="160rpx" @click="click"></image>
+      </scroll-view>
+
+      <!-- 右侧分类内容 -->
+      <scroll-view scroll-y="true" class="box-right">
+        <view class="category-image">
+          <image :showLoading="true" :src="categoryList[currentIndex].bannerUrl" mode='widthFix' @click="click"></image>
+        </view>
+
         <view class="sub-category-box" v-for="(item, index) in categoryList[currentIndex].children" :key="item.id">
           <view class="sub-category-header">
-            <view class="title">{{ item.title }}</view>
-            <view class="more">查看更多</view>
+            <view class="title">{{ item.name }}</view>
+            <view class="more" @click="handleCategory(item, 0)">查看更多</view>
+          </view>
+
+          <view class="sub-category-grid">
+            <u-grid col="3">
+              <u-grid-item v-for="(subItem, subIndex) in item.children" :key="subItem.id">
+                <view class="sub-category-item" @click="handleCategory(item, subIndex)">
+                  <u-icon name="photo" :size="80" v-if="subItem.bannerUrl === null"></u-icon>
+                  <image :src="item.bannerUrl" v-if="subItem.bannerUrl != null" mode='widthFix' />
+                  <text class="sub-category-title">{{ subItem.name }}</text>
+                </view>
+              </u-grid-item>
+            </u-grid>
           </view>
-          <u-grid class="sub-category-grid" col="3">
-            <u-grid-item v-for="(subItem, subIndex) in item.category" :key="subItem.id">
-              <view class="sub-category-item">
-                <u-icon name="photo" :size="80"></u-icon>
-                <text class="sub-category-title">{{ subItem.title }}</text>
-              </view>
-            </u-grid-item>
-          </u-grid>
         </view>
-      </view>
+      </scroll-view>
     </view>
   </view>
 </template>
 
 <script>
-export default {
-  data() {
-    return {
-      currentIndex: 0,
-      categoryList: []
-    }
-  },
-  onLoad() {
-    for (let i = 0; i < 10; i++) {
-      this.categoryList.push({
-        id: i,
-        image: 'https://cdn.uviewui.com/uview/swiper/swiper1.png',
-        name: '商品分类' + i,
-        children: [
-          {
-            id: 0,
-            title: '分类' + i + '-1',
-            category: [
-              {
-                id: 0,
-                image: '',
-                title: '分类' + i + '-1-1'
-              },
-              {
-                id: 2,
-                image: '',
-                title: '分类' + i + '-1-2'
-              },
-              {
-                id: 3,
-                image: '',
-                title: '分类' + i + '-1-3'
-              }
-            ]
-          },
-          {
-            id: 1,
-            title: '分类' + i + '-2',
-            category: [
-              {
-                id: 0,
-                image: '',
-                title: '分类' + i + '-2-1'
-              },
-              {
-                id: 2,
-                image: '',
-                title: '分类' + i + '-2-2'
-              },
-              {
-                id: 3,
-                image: '',
-                title: '分类' + i + '-2-3'
-              }
-            ]
-          }
-        ]
-      })
-    }
-  },
-  methods: {
-    handleSearchClick(e) {
-      uni.$u.route('/pages/search/search')
+  import { categoryListData } from '../../api/category';
+  import { handleTree, convertTree } from '../../utils/tree.js';
+
+  export default {
+    data() {
+      return {
+        currentIndex: 0,
+        categoryList: []
+      }
     },
-    handleCategoryClick(index) {
-      if (this.currentIndex !== index) {
-        this.currentIndex = index
+    onLoad() {
+      this.handleCategoryList();
+    },
+    methods: {
+      // 点击搜索框
+      handleSearchClick(e) {
+        uni.$u.route('/pages/search/search')
+      },
+      // 点击左侧导航栏
+      handleCategoryClick(index) {
+        if (this.currentIndex !== index) {
+          this.currentIndex = index
+        }
+      },
+      // 获取分类列表并构建树形结构
+      handleCategoryList() {
+        categoryListData().then(res => {
+          this.categoryList = handleTree(res.data, "id", "parentId");
+        })
+      },
+      handleCategory(item, index){
+        // console.log(item)
+        // console.log(index)
+        uni.navigateTo({
+          url:"./product-list?item="+encodeURIComponent(JSON.stringify(item))+"&index="+index
+        })
       }
     }
   }
-}
 </script>
 
 <style lang="scss" scoped>
+  .search-wrap {
+    background: #ffffff;
+    position: fixed;
+    top: 0;
+    left: 0;
+    box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.07);
+    padding: 20rpx 0;
+    width: 100%;
+    z-index: 3;
+  }
+
+  .category-box {
+    position: fixed;
+    display: flex;
+    overflow: hidden;
+    margin-top: 100rpx;
+    height: calc(100% - 100rpx);
+
+    .box-left {
+      width: 200rpx;
+      padding-top: 5rpx;
+      overflow: scroll;
+      z-index: 2;
+      background-color: #f2f2f2;
+
+      .category-item {
+        line-height: 80rpx;
+        height: 80rpx;
+        text-align: center;
+        color: #777;
 
-.search-wrap {
-  background: $custom-bg-color;
-  padding: 20rpx;
-}
-
-.category-box {
-  display: flex;
-  .box-left {
-    width: 200rpx;
-    padding-top: 20rpx;
-    border-right: $custom-border-style;
-    .category-item {
-      border-bottom: $custom-border-style;
-      padding: 20rpx 0;
-      .item-title {
-        padding-left: 30rpx;
-        font-size: 28rpx;
-        &.active {
-          border-left: 6rpx solid $u-primary;
-          font-weight: 700;
+        .item-title {
+          font-size: 28rpx;
+
+          &.active {
+            font-size: 28rpx;
+            font-weight: bold;
+            position: relative;
+            background: #fff;
+            color: $u-primary;
+          }
+
+          &.active::before {
+            position: absolute;
+            left: 0;
+            content: "";
+            width: 8rpx;
+            height: 32rpx;
+            top: 25rpx;
+            background: $u-primary;
+          }
         }
       }
     }
-  }
-  .box-right {
-    flex: 1;
-    .category-image {
-      width: 510rpx;
-      height: 160rpx;
-      padding: 20rpx;
-    }
 
-    .sub-category-box {
-      .sub-category-header {
-        @include flex-space-between;
-        padding: 30rpx 20rpx;
+    .box-right {
+      width: 550rpx;
+      height: 100%;
+      box-sizing: border-box;
+      z-index: 1;
 
-        .title {
-          font-size: 28rpx;
-          font-weight: 700;
-        }
-        .more {
-          font-size: 22rpx;
-          color: #939393;
+      .category-image {
+        width: 510rpx;
+        box-sizing: border-box;
+        overflow: hidden;
+        position: relative;
+        margin: 30rpx 20rpx 0;
+
+        image {
+          width: 100%;
         }
       }
 
-      .sub-category-grid {
-        padding: 0 15rpx;
+      .sub-category-box {
+        .sub-category-header {
+          @include flex-space-between;
+          padding: 20rpx 20rpx;
+
+          .title {
+            font-size: 28rpx;
+            font-weight: bolder;
+          }
+
+          .more {
+            font-size: 22rpx;
+            color: #939393;
+          }
+        }
+
+        .sub-category-grid {
+          padding: 0 15rpx;
+
+          .sub-category-item {
+            @include flex-center(column);
+            background: #fff;
 
-        .sub-category-item {
-          @include flex-center(column);
-          background: #fff;
+            image {
+              text-align: center;
+              width: 150rpx;
+              height: 150rpx;
+              line-height: 150rpx;
+              font-size: 0;
+            }
 
-          .sub-category-title {
-            margin: 15rpx 0;
-            font-size: 24rpx;
+            .sub-category-title {
+              margin: 15rpx 0;
+              font-size: 22rpx;
+            }
           }
         }
       }
     }
   }
-}
 </style>

+ 161 - 0
yudao-ui-app/pages/category/product-list.vue

@@ -0,0 +1,161 @@
+<template>
+  <view class="container">
+    <u-navbar :title="title" :autoBack="true" placeholder="true" titleStyle="font-size: 28rpx">
+    </u-navbar>
+    <view class="context">
+      <view class="tabs-top">
+        <u-tabs :list="categoryList" @click="changeTabs" :current="current" lineHeight="2" lineWidth="85rpx"
+          itemStyle="padding-left: 15px; padding-right: 15px; height: 85rpx;"></u-tabs>
+      </view>
+      <scroll-view scroll-y="true" class="product-list" enable-flex="true">
+        <view class="flex-box">
+          <block v-for="(item, index) in productList[current]" :key="index">
+            <view class="product-item">
+              <view class="product-image">
+                <image :src="item.picUrls[0]" mode='widthFix' />
+              </view>
+              <view class="product-button">
+                <view class="product-text">【{{ item.sellPoint }}】{{ item.name }}</view>
+                <view class="product-price-button">
+                  <text class="product-price">¥
+                    <text class="price-size">{{ towNumber(item.price) }}</text></text>
+                  <text class="product-like-ccount">销量 {{ item.likeCount }}</text>
+                </view>
+              </view>
+            </view>
+          </block>
+        </view>
+      </scroll-view>
+    </view>
+  </view>
+</template>
+
+<script>
+  import {
+    productSpuPage
+  } from '../../api/product';
+
+  export default {
+    data() {
+      return {
+        title: "",
+        current: 0,
+        categoryList: [],
+        productList: {}
+      }
+    },
+    onLoad(option) {
+      const item = JSON.parse(decodeURIComponent(option.item))
+      this.title = item.name
+      this.categoryList = item.children
+      this.handleProductSpu(option.index);
+    },
+    methods: {
+      changeTabs(item) {
+        if (item.index != this.current) {
+          this.handleProductSpu(item.index)
+        }
+      },
+      handleProductSpu(index) {
+        let param = {}
+        param.categoryId = this.categoryList[index].id
+        console.log(this.categoryList)
+        console.log(index)
+        productSpuPage(param).then(res => {
+          this.productList[index] = res.data.list
+          this.current = index
+        })
+      },
+      towNumber(val) {
+        return (val / 100).toFixed(2)
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .context {
+    width: 100vw;
+    position: fixed;
+    top: 160rpx;
+    left: 0;
+  }
+
+  .tabs-top {
+    position: relative;
+    top: 0;
+    width: 100%;
+    height: 85rpx;
+  }
+
+  .product-list {
+    position: relative;
+    background-color: #f2f2f2;
+    height: calc(100vh - 88rpx - 100rpx - var(--status-bar-height));
+    width: 100%;
+
+    .flex-box {
+      width: 730rpx;
+      margin: 0 auto;
+      @include flex;
+      flex-wrap: wrap;
+      justify-content: left;
+
+      .product-item {
+        width: 345rpx;
+        height: 450rpx;
+        background-color: #ffffff;
+        margin: 20rpx 10rpx 0;
+        border-radius: 20rpx;
+
+        .product-image {
+          width: 100%;
+          height: 300rpx;
+          overflow: hidden;
+          border-radius: 20rpx;
+          image {
+            width: 100%;
+          }
+        }
+
+        .product-button {
+          width: 330rpx;
+          margin: 15rpx auto 0;
+
+          .product-text {
+            font-size: 25rpx;
+            height: 70rpx;
+            overflow: hidden;
+            -webkit-line-clamp: 2;
+            text-overflow: ellipsis;
+            display: -webkit-box;
+            -webkit-box-orient: vertical;
+          }
+
+          .product-price-button {
+            font-size: 20rpx;
+            margin-top: 20rpx;
+
+            .product-price {
+              color: red;
+
+              .price-size {
+                font-size: 26rpx;
+              }
+            }
+
+            .product-like-ccount {
+              font-size: 16rpx;
+              margin-left: 10rpx;
+            }
+          }
+
+        }
+      }
+
+    }
+
+    // }
+
+  }
+</style>