7.进行调试
7.1 运行后台和hbuilder 进行调试
发现不能正常显示产品列表信息,问题出在下面的函数
#region 获取所有商品分类栏目数据2025
/// <summary>
/// 获取所有商品分类栏目数据 ,并显示第一页产品信息
///
/// </summary>
/// <returns>
/// 1.一个大类列表,并指定一个当前类目(选一个为当前类目,选择条件为第一个非空类目,即默认第一个大类的类目是当前类目,
/// 如果第一个大类的类目下面产品是空的,就选择第二个类目为当前类目,
/// 如果第二个类目下面产品还是空的,就继续往下选择,
/// 如果全部类目下面都产品都是空的,还是选择第一个类目为当前类目)。
///2. 返回 “当前类目”之下的的小类列表和部分产品信息。
/// </returns>
[HttpPost]
public async Task<WebApiCallBack> GetAllCategories2025()
{
var jm = new WebApiCallBack() { status = true };
//1.取得所有的分类 ,并以父子关系进行分类
var data = await _goodsCategoryServices.QueryListByClauseAsync(p => p.isShow == true, p => p.sort,
OrderByType.Asc);
var wxGoodCategoryDto = new List<WxGoodCategoryDto>();//顶级分类
var parents = data.Where(p => p.parentId == 0).ToList();
if (parents.Any())
{
parents.ForEach(p =>
{
var model = new WxGoodCategoryDto();
model.id = p.id;
model.name = p.name;
model.imageUrl = !string.IsNullOrEmpty(p.imageUrl) ? p.imageUrl : "/static/images/common/empty.png";
model.sort = p.sort;
var childs = data.Where(p => p.parentId == model.id).ToList();
if (childs.Any())
{
var childsList = new List<WxGoodCategoryChild>();
childs.ForEach(o =>
{
childsList.Add(new WxGoodCategoryChild()
{
id = o.id,
imageUrl = !string.IsNullOrEmpty(o.imageUrl) ? o.imageUrl : "/static/images/common/empty.png",
name = o.name,
sort = o.sort
});
});
model.child = childsList;
}
// 下面这列就是 返回的 大类列表 (也就是顶级类目)
wxGoodCategoryDto.Add(model);
});
}
//
//2。取得一第大类的产品信息
// 我们默认产品信息显示区,能显示18个产品,一行3个,6行。
CategoryGoodsDetail currentCategoryGoodsDetail=null;
if (wxGoodCategoryDto.Count > 0)
{
//2.0 取得第一个大类之下的产品信息。查出 18个个产品。
//如果 超过18个,我只则只取前18个。第19个显示 一个更多按钮
bool hasGoods = false; //当前类目下是否有产品
int icurrentCategoryIndex = 0;
do
{
//初始化 当前类目信息
//在 微信商城界面上是 产品列表界面 右边的 产品信息区
currentCategoryGoodsDetail = new CategoryGoodsDetail();
int CategoryID = wxGoodCategoryDto[icurrentCategoryIndex].id;
WxGoodCategoryDto currentTopCategory = wxGoodCategoryDto[icurrentCategoryIndex];
//2.1大类之下的产品
var bigCategoerGoods = _goodsServices.QueryPage( good=> ! good.isDel && good. );
//.QueryListByClause(" isnull(isDel,0) <>1 and [goodsCategoryId]=" + CategoryID.ToString ());
/*await _goodsServices.GetGoodsInfoList(
p => p.isMarketable == true && p.isDel == false && p.goodsCategoryId == CategoryID
, null, null, 1, 18, false);*/
if (bigCategoerGoods.Count > 0)
{
hasGoods = true; //当前类目下有产品
}
currentCategoryGoodsDetail.curentCategory = currentTopCategory;
currentCategoryGoodsDetail.TopCategoryGoodsInfoList = bigCategoerGoods;
//2.2 取得大类下面的子类列表
if (currentTopCategory.child.Count > 0)
{
foreach (var child in currentTopCategory.child)
{
var thisCategoerGoods = await _goodsServices.GetGoodsInfoList(
p => p.isMarketable == true && p.isDel == false && p.goodsCategoryId == child.id
, null, null, 1, 18, false);
subCategoryGoodsDetail subCategoryGoodsDetailTemp = new subCategoryGoodsDetail();
subCategoryGoodsDetailTemp.GoodsInfoList = thisCategoerGoods;
subCategoryGoodsDetailTemp.subCategory = new WxGoodCategoryDto();
subCategoryGoodsDetailTemp.subCategory.id = child.id;
subCategoryGoodsDetailTemp.subCategory.name = child.name;
currentCategoryGoodsDetail.subGoodsInfoList.Add(subCategoryGoodsDetailTemp);
hasGoods = true; //当前类目下有产品 有子类也算有产品
}
if (hasGoods)
{
//找到了第一个有产品的类目,就退出循环
break;
}
}
icurrentCategoryIndex++;
}
while (icurrentCategoryIndex < wxGoodCategoryDto.Count && !hasGoods );
}
jm.status = true;
jm.data = new
{
wxGoodCategoryDto = wxGoodCategoryDto,
currentCategoryGoodsDetail = currentCategoryGoodsDetail
};
return jm;
}
主要问题就是这行代码出错
//2.1大类之下的产品
var bigCategoerGoods = await _goodsServices.GetGoodsInfoList(
p => p.isMarketable == true && p.isDel == false && p.goodsCategoryId == CategoryID
, null, null, 1, 18, false);
这个是咱们新增加的函数,通过观察整个框架,我发现原来提供了查询功能,虽然跟我们的查询功能有差距,但是,我们应该遵循开闭原则,即对扩展开放,当需求变化或需要新功能时,可能通过扩展已有的代码(如继承、组合、依赖注入)来实现新行为;对修改关闭,不修改已稳定、经过测试的源代码,尤其是那些核心、底层的函数或类。修改原有代码可能会引入错误,破坏现在功能。
修改之后的函数代码如下:
#region 获取所有商品分类栏目数据2025
/// <summary>
/// 获取所有商品分类栏目数据 ,并显示第一页产品信息
///
/// </summary>
/// <returns>
/// 1.一个大类列表,并指定一个当前类目(选一个为当前类目,选择条件为第一个非空类目,即默认第一个大类的类目是当前类目,
/// 如果第一个大类的类目下面产品是空的,就选择第二个类目为当前类目,
/// 如果第二个类目下面产品还是空的,就继续往下选择,
/// 如果全部类目下面都产品都是空的,还是选择第一个类目为当前类目)。
///2. 返回 “当前类目”之下的的小类列表和部分产品信息。
/// </returns>
[HttpPost]
public async Task<WebApiCallBack> GetAllCategories2025()
{
var jm = new WebApiCallBack() { status = true };
//1.取得所有的分类 ,并以父子关系进行分类
var data = await _goodsCategoryServices.QueryListByClauseAsync(p => p.isShow == true, p => p.sort,
OrderByType.Asc);
var wxGoodCategoryDto = new List<WxGoodCategoryDto>();//顶级分类
var parents = data.Where(p => p.parentId == 0).ToList();
if (parents.Any())
{
parents.ForEach(p =>
{
var model = new WxGoodCategoryDto();
model.id = p.id;
model.name = p.name;
model.imageUrl = !string.IsNullOrEmpty(p.imageUrl) ? p.imageUrl : "/static/images/common/empty.png";
model.sort = p.sort;
var childs = data.Where(p => p.parentId == model.id).ToList();
if (childs.Any())
{
var childsList = new List<WxGoodCategoryChild>();
childs.ForEach(o =>
{
childsList.Add(new WxGoodCategoryChild()
{
id = o.id,
imageUrl = !string.IsNullOrEmpty(o.imageUrl) ? o.imageUrl : "/static/images/common/empty.png",
name = o.name,
sort = o.sort
});
});
model.child = childsList;
}
// 下面这列就是 返回的 大类列表 (也就是顶级类目)
wxGoodCategoryDto.Add(model);
});
}
//
//2。取得一第大类的产品信息
// 我们默认产品信息显示区,能显示18个产品,一行3个,6行。
CategoryGoodsDetail currentCategoryGoodsDetail=null;
if (wxGoodCategoryDto.Count > 0)
{
//2.0 取得第一个大类之下的产品信息。查出 18个个产品。
//如果 超过18个,我只则只取前18个。第19个显示 一个更多按钮
bool hasGoods = false; //当前类目下是否有产品
int icurrentCategoryIndex = 0;
do
{
//初始化 当前类目信息
//在 微信商城界面上是 产品列表界面 右边的 产品信息区
currentCategoryGoodsDetail = new CategoryGoodsDetail();
int CategoryID = wxGoodCategoryDto[icurrentCategoryIndex].id;
WxGoodCategoryDto currentTopCategory = wxGoodCategoryDto[icurrentCategoryIndex];
//2.1大类之下的产品
var bigCategoerGoods = _goodsServices.QueryPage( good=> ! good.isDel && good.goodsCategoryId== CategoryID,"",1,18);
List<CoreGoodsInfo> bigGoods = new List<CoreGoodsInfo>();
//进行类型转换
if ( bigCategoerGoods.Any())
{
foreach(var good in bigCategoerGoods)
{
CoreGoodsInfo coreGoodsInfo = new CoreGoodsInfo();
coreGoodsInfo.id = good.id;
coreGoodsInfo.name = good.name;
coreGoodsInfo.price = good.price;
coreGoodsInfo.mktprice= good.mktprice;
coreGoodsInfo.image = good.image;
coreGoodsInfo.isRecommend = good.isRecommend;
bigGoods.Add(coreGoodsInfo);
hasGoods = true; //当前类目下有产品
}
}
currentCategoryGoodsDetail.curentCategory = currentTopCategory;
currentCategoryGoodsDetail.TopCategoryGoodsInfoList = bigGoods;
//2.2 取得大类下面的子类列表
if (currentTopCategory.child.Count > 0)
{
foreach (var child in currentTopCategory.child)
{
var thisCategoerGoods = _goodsServices.QueryPage(good => !good.isDel && good.goodsCategoryId == child.id, "", 1, 18);
subCategoryGoodsDetail subCategoryGoodsDetailTemp = new subCategoryGoodsDetail();
currentCategoryGoodsDetail.subGoodsInfoList = new List<subCategoryGoodsDetail>();
List<CoreGoodsInfo> subGoods = new List<CoreGoodsInfo>();
//进行类型转换
if (bigCategoerGoods.Any())
{
foreach (var good in bigCategoerGoods)
{
CoreGoodsInfo coreGoodsInfo = new CoreGoodsInfo();
coreGoodsInfo.id = good.id;
coreGoodsInfo.name = good.name;
coreGoodsInfo.price = good.price;
coreGoodsInfo.mktprice = good.mktprice;
coreGoodsInfo.image = good.image;
coreGoodsInfo.isRecommend = good.isRecommend;
subGoods.Add(coreGoodsInfo);
hasGoods = true; //当前类目下有产品
}
}
subCategoryGoodsDetailTemp.GoodsInfoList = subGoods;
subCategoryGoodsDetailTemp.subCategory = new WxGoodCategoryDto();
subCategoryGoodsDetailTemp.subCategory.id = child.id;
subCategoryGoodsDetailTemp.subCategory.name = child.name;
currentCategoryGoodsDetail.subGoodsInfoList.Add(subCategoryGoodsDetailTemp);
hasGoods = true; //当前类目下有产品 有子类也算有产品
}
if (hasGoods)
{
//找到了第一个有产品的类目,就退出循环
break;
}
}
icurrentCategoryIndex++;
}
while (icurrentCategoryIndex < wxGoodCategoryDto.Count && !hasGoods );
}
jm.status = true;
jm.data = new
{
wxGoodCategoryDto = wxGoodCategoryDto,
currentCategoryGoodsDetail = currentCategoryGoodsDetail
};
return jm;
}
#endregion
实体类定义如下:
/***********************************************************************
* Project: CoreCms
* ProjectName: 核心内容管理系统
* Web: https://www.corecms.net
* Author: 大灰灰
* Email: jianweie@163.com
* CreateTime: 2021-06-08 22:14:58
* Description: 暂无
***********************************************************************/
using CoreCms.Net.Model.ViewModels.DTO;
using SqlSugar;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace CoreCms.Net.Model.Entities
{
/// <summary>
/// 商品信息表 返回微信小程序端所需字段
/// </summary>
public class CoreGoodsInfo
{
/// <summary>
/// 商品ID
/// </summary>
public int id { get; set; }
/// <summary>
/// 商品名称
/// </summary>
public string name { get; set; }
/// <summary>
/// 商品主图
/// </summary>
public string image { get; set; }
/// <summary>
/// 销售价格
/// </summary>
public decimal price { get; set; }
/// <summary>
/// 市场价格
/// </summary>
public decimal mktprice { get; set; }
/// <summary>
/// 是否推荐
/// </summary>
public bool isRecommend { get; set; }
/// <summary>
/// 是否热门
/// </summary>
public bool isHot { get; set; }
/// <summary>
/// 评论数量
/// </summary>
public int commentsCount { get; set; }
/// <summary>
/// 销量
/// </summary>
public int buyCount { get; set; }
/// <summary>
/// 排序值
/// </summary>
public int sort { get; set; }
}
/// <summary>
/// 子分类商品详情DTO
/// </summary>
public class SubCategoryGoodsDetail
{
/// <summary>
/// 子分类信息
/// </summary>
public WxGoodCategoryDto subCategory { get; set; }
/// <summary>
/// 该子分类下的商品列表
/// </summary>
public List<CoreGoodsInfo> GoodsInfoList { get; set; } = new List<CoreGoodsInfo>();
/// <summary>
/// 是否展开(前端使用)
/// </summary>
public bool expanded { get; set; }
}
/// <summary>
/// 分类商品详情DTO
/// </summary>
public class CategoryGoodsDetail
{
/// <summary>
/// 当前分类信息
/// </summary>
public WxGoodCategoryDto curentCategory { get; set; }
/// <summary>
/// 顶级分类下的商品列表
/// </summary>
public List<CoreGoodsInfo> TopCategoryGoodsInfoList { get; set; } = new List<CoreGoodsInfo>();
/// <summary>
/// 子分类商品列表
/// </summary>
public List<SubCategoryGoodsDetail> subGoodsInfoList { get; set; } = new List<SubCategoryGoodsDetail>();
}
/// <summary>
/// 分类页面数据DTO
/// </summary>
public class CategoryData
{
/// <summary>
/// 顶级分类列表
/// </summary>
public List<WxGoodCategoryDto> wxGoodCategoryDto { get; set; }
/// <summary>
/// 当前分类的商品详情
/// </summary>
public CategoryGoodsDetail currentCategoryGoodsDetail { get; set; }
}
}
程序所涉及的实体类还有:
/***********************************************************************
* Project: CoreCms
* ProjectName: 核心内容管理系统
* Web: https://www.corecms.net
* Author: 大灰灰
* Email: jianweie@163.com
* CreateTime: 2021/7/16 1:14:14
* Description: 暂无
***********************************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Security.Claims;
using SqlSugar;
namespace CoreCms.Net.Model.Entities
{
/// <summary>
/// 商品表
/// </summary>
public partial class CoreCmsGoods
{
/// <summary>
/// 商品ID
/// </summary>
[Display(Name = "商品ID")]
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
[Required(ErrorMessage = "请输入{0}")]
public int id { get; set; }
/// <summary>
/// 商品条码
/// </summary>
[Display(Name = "商品条码")]
[Required(ErrorMessage = "请输入{0}")]
[StringLength(30, ErrorMessage = "{0}不能超过{1}字")]
public string bn { get; set; }
/// <summary>
/// 商品条码
/// </summary>
[Display(Name = "原厂编码")]
[StringLength(30, ErrorMessage = "{0}不能超过{1}字")]
public string oeMcode { get; set; }
/// <summary>
/// 商品名称
/// </summary>
[Display(Name = "商品名称")]
[Required(ErrorMessage = "请输入{0}")]
[StringLength(200, ErrorMessage = "{0}不能超过{1}字")]
public string name { get; set; }
/// <summary>
/// 商品简介
/// </summary>
[Display(Name = "商品简介")]
[StringLength(255, ErrorMessage = "{0}不能超过{1}字")]
public string brief { get; set; }
/// <summary>
/// 缩略图
/// </summary>
[Display(Name = "缩略图")]
[StringLength(255, ErrorMessage = "{0}不能超过{1}字")]
public string image { get; set; }
/// <summary>
/// 图集
/// </summary>
[Display(Name = "图集")]
public string images { get; set; }
/// <summary>
/// 视频
/// </summary>
[Display(Name = "视频")]
[StringLength(255, ErrorMessage = "{0}不能超过{1}字")]
public string video { get; set; }
/// <summary>
/// 佣金分配方式
/// </summary>
[Display(Name = "佣金分配方式")]
[Required(ErrorMessage = "请输入{0}")]
public int productsDistributionType { get; set; }
/// <summary>
/// 商品分类
/// </summary>
[Display(Name = "商品分类")]
[Required(ErrorMessage = "请输入{0}")]
public int goodsCategoryId { get; set; }
/// <summary>
/// 商品类别
/// </summary>
[Display(Name = "商品类别")]
[Required(ErrorMessage = "请输入{0}")]
public int goodsTypeId { get; set; }
/// <summary>
/// sku序列
/// </summary>
[Display(Name = "sku序列")]
[StringLength(255, ErrorMessage = "{0}不能超过{1}字")]
public string goodsSkuIds { get; set; }
/// <summary>
/// 参数序列
/// </summary>
[Display(Name = "参数序列")]
[StringLength(255, ErrorMessage = "{0}不能超过{1}字")]
public string goodsParamsIds { get; set; }
/// <summary>
/// 品牌
/// </summary>
[Display(Name = "品牌")]
[Required(ErrorMessage = "请输入{0}")]
public int brandId { get; set; }
/// <summary>
/// 是否虚拟商品
/// </summary>
[Display(Name = "是否虚拟商品")]
[Required(ErrorMessage = "请输入{0}")]
public bool isNomalVirtual { get; set; }
/// <summary>
/// 是否上架
/// </summary>
[Display(Name = "是否上架")]
[Required(ErrorMessage = "请输入{0}")]
public bool isMarketable { get; set; }
/// <summary>
/// 商品单位
/// </summary>
[Display(Name = "商品单位")]
[StringLength(20, ErrorMessage = "{0}不能超过{1}字")]
public string unit { get; set; }
/// <summary>
/// 商品详情
/// </summary>
[Display(Name = "商品详情")]
public string intro { get; set; }
/// <summary>
/// 商品规格序列号存储
/// </summary>
[Display(Name = "商品规格序列号存储")]
public string spesDesc { get; set; }
/// <summary>
/// 参数序列化
/// </summary>
[Display(Name = "参数序列化")]
public string parameters { get; set; }
/// <summary>
/// 评论次数
/// </summary>
[Display(Name = "评论次数")]
[Required(ErrorMessage = "请输入{0}")]
public int commentsCount { get; set; }
/// <summary>
/// 浏览次数
/// </summary>
[Display(Name = "浏览次数")]
[Required(ErrorMessage = "请输入{0}")]
public int viewCount { get; set; }
/// <summary>
/// 购买次数
/// </summary>
[Display(Name = "购买次数")]
[Required(ErrorMessage = "请输入{0}")]
public int buyCount { get; set; }
/// <summary>
/// 上架时间
/// </summary>
[Display(Name = "上架时间")]
public DateTime? uptime { get; set; }
/// <summary>
/// 下架时间
/// </summary>
[Display(Name = "下架时间")]
public DateTime? downtime { get; set; }
/// <summary>
/// 商品排序
/// </summary>
[Display(Name = "商品排序")]
[Required(ErrorMessage = "请输入{0}")]
public int sort { get; set; }
/// <summary>
/// 标签id逗号分隔
/// </summary>
[Display(Name = "标签id逗号分隔")]
[StringLength(50, ErrorMessage = "{0}不能超过{1}字")]
public string labelIds { get; set; }
/// <summary>
/// 自定义规格名称
/// </summary>
[Display(Name = "自定义规格名称")]
public string newSpec { get; set; }
/// <summary>
/// 开启规则
/// </summary>
[Display(Name = "开启规则")]
[Required(ErrorMessage = "请输入{0}")]
public int openSpec { get; set; }
/// <summary>
/// 创建时间
/// </summary>
[Display(Name = "创建时间")]
public DateTime? createTime { get; set; }
/// <summary>
/// 更新时间
/// </summary>
[Display(Name = "更新时间")]
public DateTime? updateTime { get; set; }
/// <summary>
/// 是否推荐
/// </summary>
[Display(Name = "是否推荐")]
[Required(ErrorMessage = "请输入{0}")]
public bool isRecommend { get; set; }
/// <summary>
/// 是否热门
/// </summary>
[Display(Name = "是否热门")]
[Required(ErrorMessage = "请输入{0}")]
public bool isHot { get; set; }
/// <summary>
/// 是否删除
/// </summary>
[Display(Name = "是否删除")]
[Required(ErrorMessage = "请输入{0}")]
public bool isDel { get; set; }
/// <summary>
/// 店铺id
/// </summary>
[Display(Name = "店铺ID")]
public int storeId { get; set; }
}
}
8。修改之后的代码
8.1微信小程序端代码如下:
<template>
<view class="u-wrap">
<u-toast ref="uToast" />
<u-no-network></u-no-network>
<u-navbar :is-back="false" :background="background">
<view class="slot-wrap">
<u-search :show-action="true" shape="round" v-model="searchKey" action-text="搜索" placeholder="请输入搜索内容" @custom="goSearch" @search="goSearch" :action-style="actionStyle"></u-search>
</view>
</u-navbar>
<view v-if="categoryList.length === 0" class="empty-category">
<u-empty :src="$globalConstVars.apiFilesUrl+'/static/images/common/empty.png'" icon-size="300" text="暂无商品分类" mode="list"></u-empty>
</view>
<view class="u-menu-wrap" v-else>
<!-- 左边大类菜单 -->
<scroll-view scroll-y scroll-with-animation class="u-tab-view menu-scroll-view" :scroll-top="scrollTop">
<view v-for="(item,index) in categoryList" :key="index" class="u-tab-item" :class="[currentCategory==index ? 'u-tab-item-active' : '']"
:data-current="index" @tap.stop="switchCategory(index,item.id)">
<text class="u-line-1">{{item.name}}</text>
</view>
</scroll-view>
<scroll-view scroll-y class="right-box">
<!-- 当前大类的信息 -->
<view v-if="currentCategoryData" class="current-category-info">
<!-- 调试信息 -->
<view class="debug-info" style="padding: 20rpx; background: #f5f5f5; color: #666; font-size: 24rpx;">
<text>大类商品数: {{currentCategoryData.topCategoryGoodsInfoList ? currentCategoryData.topCategoryGoodsInfoList.length : 0}}</text>
<text> | 子分类数: {{currentCategoryData.subGoodsInfoList ? currentCategoryData.subGoodsInfoList.length : 0}}</text>
<text> | 当前分类: {{currentCategory}}</text>
</view>
<!-- 大类标题 -->
<view class="category-title">{{currentCategoryData.curentCategory && currentCategoryData.curentCategory.name}}</view>
<!-- 1. 大类下的直接商品 -->
<view v-if="currentCategoryData.topCategoryGoodsInfoList && currentCategoryData.topCategoryGoodsInfoList.length > 0" class="category-section">
<view class="section-title">本类热门商品 (共{{currentCategoryData.topCategoryGoodsInfoList.length}}件)</view>
<view class="goods-grid">
<view class="goods-item" v-for="(item, index) in currentCategoryData.topCategoryGoodsInfoList" :key="index" @click="goGoodsDetail(item.id)">
<view class="good_box">
<u-lazy-load threshold="-150" border-radius="10" :image="item.image" :index="index"></u-lazy-load>
<view class="good_title u-line-2">
{{item.name || '未知商品'}}
</view>
<view class="good-price">
{{item.price || 0}}元
<span class="u-font-xs coreshop-text-through u-margin-left-15 coreshop-text-gray">
{{item.mktprice || 0}}元
</span>
</view>
<view class="good-tag-recommend" v-if="item.isRecommend">
推荐
</view>
<view class="good-tag-hot" v-if="item.isHot">
热门
</view>
</view>
</view>
</view>
</view>
<!-- 如果没有大类商品,显示提示 -->
<view v-else-if="currentCategoryData.topCategoryGoodsInfoList && currentCategoryData.topCategoryGoodsInfoList.length === 0" class="no-goods-tip">
<text>该大类下暂无直接商品</text>
</view>
<!-- 2. 子分类列表 -->
<view v-if="currentCategoryData.subGoodsInfoList && currentCategoryData.subGoodsInfoList.length > 0" class="subcategory-list-section">
<view class="section-title">子分类</view>
<view class="subcategory-grid">
<view v-for="(subCategory, subIndex) in currentCategoryData.subGoodsInfoList" :key="subIndex"
class="subcategory-item" @click="toggleSubCategory(subIndex)">
<view class="subcategory-content">
<view class="subcategory-name">{{subCategory.subCategory && subCategory.subCategory.name}}</view>
<view class="subcategory-count">共{{subCategory.goodsInfoList ? subCategory.goodsInfoList.length : 0}}件商品</view>
<view class="subcategory-arrow" :class="{'subcategory-arrow-up': subCategory.expanded}">
<u-icon name="arrow-down" size="24"></u-icon>
</view>
</view>
<!-- 3. 子分类下的商品列表(可折叠) -->
<view v-if="subCategory.expanded && subCategory.goodsInfoList && subCategory.goodsInfoList.length > 0" class="subcategory-goods">
<view class="goods-grid">
<view class="goods-item" v-for="(item, index) in subCategory.goodsInfoList" :key="index" @click.stop="goGoodsDetail(item.id)">
<view class="good_box">
<u-lazy-load threshold="-150" border-radius="10" :image="item.image" :index="index"></u-lazy-load>
<view class="good_title u-line-2">
{{item.name}}
</view>
<view class="good-price">
{{item.price}}元 <span class="u-font-xs coreshop-text-through u-margin-left-15 coreshop-text-gray">{{item.mktprice}}元</span>
</view>
<view class="good-tag-recommend" v-if="item.isRecommend">
推荐
</view>
<view class="good-tag-hot" v-if="item.isHot">
热门
</view>
</view>
</view>
</view>
</view>
<!-- 子分类为空的状态 -->
<view v-else-if="subCategory.expanded && (!subCategory.goodsInfoList || subCategory.goodsInfoList.length === 0)" class="empty-subcategory-goods">
<u-empty :src="$globalConstVars.apiFilesUrl+'/static/images/common/empty.png'" icon-size="120" :text="(subCategory.subCategory && subCategory.subCategory.name) + '下暂无商品'" mode="list"></u-empty>
</view>
</view>
</view>
</view>
<!-- 如果没有商品显示空状态 -->
<view v-if="(!currentCategoryData.topCategoryGoodsInfoList || currentCategoryData.topCategoryGoodsInfoList.length === 0) && (!currentCategoryData.subGoodsInfoList || currentCategoryData.subGoodsInfoList.length === 0)" class="empty-category-goods">
<u-empty :src="$globalConstVars.apiFilesUrl+'/static/images/common/empty.png'" icon-size="300" text="该分类下暂无商品" mode="list"></u-empty>
</view>
</view>
<!-- 加载状态 -->
<view v-else class="loading-state">
<u-loading mode="circle" size="40"></u-loading>
<text class="loading-text">加载中...</text>
</view>
</scroll-view>
</view>
<!-- 在模板中添加测试按钮 -->
<view class="test-buttons" style="padding: 20rpx;">
<button @click="testAddGoods" size="mini">测试添加商品</button>
<button @click="logCurrentData" size="mini">打印当前数据</button>
</view>
</view>
</template>
<script>
import { goods } from '@/common/mixins/mixinsHelper.js';
export default {
mixins: [goods],
data() {
return {
background: {
backgroundColor: '#e54d42',
},
actionStyle: {
color: '#ffffff',
},
categoryList: [], // 大类列表
currentCategory: 0, // 当前选中大类索引
currentCategoryData: null, // 当前大类的详细数据
scrollTop: 0,
menuHeight: 0,
menuItemHeight: 0,
searchKey: '',
loading: false,
expandedSubCategories: [] // 记录展开的子分类索引
}
},
onShow() {
console.log("onShow");
this.loadAllCategories();
},
methods: {
// 加载所有分类数据
loadAllCategories() {
console.log("-----------loadAllCategories----------------");
this.loading = true;
const _this = this;
this.$u.api.getAllCategories2025().then(res => {
console.log("res=", res);
console.log("res.status",res.status);
if (res.status) {
// 直接赋值,注意字段名大小写
_this.categoryList = res.data.wxGoodCategoryDto || [];
_this.currentCategoryData = res.data.currentCategoryGoodsDetail;
console.log('分类列表:', _this.categoryList);
console.log('当前分类数据:', _this.currentCategoryData);
if (_this.currentCategoryData) {
console.log('大类商品数量:', _this.currentCategoryData.topCategoryGoodsInfoList ? _this.currentCategoryData.topCategoryGoodsInfoList.length : 0);
console.log('子分类数量:', _this.currentCategoryData.subGoodsInfoList ? _this.currentCategoryData.subGoodsInfoList.length : 0);
if (_this.currentCategoryData.topCategoryGoodsInfoList) {
console.log('大类商品详情:', _this.currentCategoryData.topCategoryGoodsInfoList);
}
}
// 初始化子分类的展开状态
if (_this.currentCategoryData && _this.currentCategoryData.subGoodsInfoList) {
_this.currentCategoryData.subGoodsInfoList.forEach((item, index) => {
_this.$set(item, 'expanded', index === 0);
});
}
// 如果后端没有返回当前分类数据,默认显示第一个分类
if (!_this.currentCategoryData && _this.categoryList.length > 0) {
_this.switchCategory(0, _this.categoryList[0].id);
}
} else {
_this.$refs.uToast.show({
title: res.msg || '加载失败',
type: 'error',
});
}
}).catch(error => {
console.log('加载分类数据失败:', error);
_this.$refs.uToast.show({
title: '网络错误,请重试',
type: 'error',
});
}).finally(() => {
_this.loading = false;
});
},
// 切换大类
async switchCategory(index, categoryId) {
console.log("switchCategory, index=", index, "categoryId=", categoryId);
if (this.loading) return;
this.currentCategory = index;
this.loading = true;
try {
// 调用接口获取指定分类的数据
// 尝试不同的接口名称
let res = null;
try {
// 先尝试 getCategoryGoods2025
res = await this.$u.api.getCategoryGoods2025({ id: categoryId });
console.log("getCategoryGoods2025 返回:", res);
} catch (error1) {
console.log("getCategoryGoods2025 接口不存在,尝试 getCategoryGoods");
try {
// 如果不存在,尝试 getCategoryGoods
res = await this.$u.api.getCategoryGoods({ categoryId: categoryId });
console.log("getCategoryGoods 返回:", res);
} catch (error2) {
console.log("getCategoryGoods 接口也不存在");
throw new Error("分类商品接口不存在");
}
}
if (res && res.status) {
// 注意:后端返回的数据结构可能是 res.data 或 res.data.currentCategoryGoodsDetail
if (res.data.currentCategoryGoodsDetail) {
this.currentCategoryData = res.data.currentCategoryGoodsDetail;
} else {
this.currentCategoryData = res.data;
}
console.log("切换分类后的数据:", this.currentCategoryData);
// 初始化子分类的展开状态
if (this.currentCategoryData && this.currentCategoryData.subGoodsInfoList) {
this.currentCategoryData.subGoodsInfoList.forEach((item, index) => {
this.$set(item, 'expanded', index === 0);
});
}
} else {
const errorMsg = res ? res.msg : '接口返回数据异常';
this.$refs.uToast.show({
title: errorMsg || '加载商品失败',
type: 'error',
});
}
} catch (error) {
console.error('加载分类商品失败:', error);
this.$refs.uToast.show({
title: '网络错误,请重试: ' + error.message,
type: 'error',
});
// 如果切换分类失败,回退到初始数据
this.currentCategoryData = null;
} finally {
this.loading = false;
}
// 滚动条定位
await this.scrollToCurrentCategory();
},
// 切换子分类展开/收起
toggleSubCategory(subIndex) {
if (this.currentCategoryData && this.currentCategoryData.subGoodsInfoList) {
const subCategory = this.currentCategoryData.subGoodsInfoList[subIndex];
this.$set(subCategory, 'expanded', !subCategory.expanded);
}
},
// 滚动条定位
async scrollToCurrentCategory() {
if (this.menuHeight === 0 || this.menuItemHeight === 0) {
await this.getElRect('menu-scroll-view', 'menuHeight');
await this.getElRect('u-tab-item', 'menuItemHeight');
}
this.scrollTop = this.currentCategory * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2;
},
getElRect(elClass, dataVal) {
return new Promise((resolve) => {
const query = uni.createSelectorQuery().in(this);
query.select('.' + elClass).fields({ size: true }, res => {
if (!res) {
setTimeout(() => {
this.getElRect(elClass, dataVal).then(resolve);
}, 10);
return;
}
this[dataVal] = res.height;
resolve();
}).exec();
});
},
goSearch() {
if (this.searchKey !== '') {
this.$u.route('/pages/category/list/list?key=' + this.searchKey);
} else {
this.$refs.uToast.show({
title: '请输入查询关键字',
type: 'warning',
});
}
},
// 跳转商品详情
goGoodsDetail(goodsId) {
this.$u.route('/pages/goods/detail/detail?id=' + goodsId);
},
// 测试添加商品数据
testAddGoods() {
if (!this.currentCategoryData) {
this.currentCategoryData = {
curentCategory: { name: '测试分类' },
topCategoryGoodsInfoList: [],
subGoodsInfoList: []
};
}
// 添加测试商品
const testGoods = {
id: 1,
name: '测试商品',
image: '/static/images/common/empty.png',
price: 99.00,
mktprice: 129.00,
isRecommend: true,
isHot: false
};
if (!this.currentCategoryData.topCategoryGoodsInfoList) {
this.$set(this.currentCategoryData, 'topCategoryGoodsInfoList', []);
}
this.currentCategoryData.topCategoryGoodsInfoList.push(testGoods);
console.log('测试商品已添加');
},
// 打印当前数据
logCurrentData() {
console.log('当前分类数据:', this.currentCategoryData);
console.log('当前分类索引:', this.currentCategory);
if (this.currentCategoryData) {
if (this.currentCategoryData.topCategoryGoodsInfoList) {
console.log('大类商品列表:', this.currentCategoryData.topCategoryGoodsInfoList);
}
if (this.currentCategoryData.subGoodsInfoList) {
console.log('子分类列表:', this.currentCategoryData.subGoodsInfoList);
}
}
}
}
}
</script>
<style lang="scss" scoped>
/* 样式保持不变,与您原来的代码一致 */
.u-wrap {
height: 100vh;
display: flex;
flex-direction: column;
}
.empty-category {
padding: 100rpx 0;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
.u-menu-wrap {
display: flex;
flex: 1;
height: 100%;
}
.u-tab-view {
width: 200rpx;
height: 100%;
background: #f8f8f8;
}
.u-tab-item {
height: 100rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
color: #666;
padding: 0 20rpx;
border-left: 4rpx solid transparent;
}
.u-tab-item-active {
background: #fff;
color: #e54d42;
border-left-color: #e54d42;
}
.right-box {
flex: 1;
height: 100%;
background: #fff;
}
.current-category-info {
min-height: 100vh;
padding-bottom: 100rpx;
}
.category-title {
padding: 30rpx;
font-size: 36rpx;
font-weight: bold;
color: #333;
background: #fff;
border-bottom: 1rpx solid #f5f5f5;
}
.section-title {
padding: 20rpx 30rpx;
font-size: 32rpx;
font-weight: bold;
color: #333;
background: #f8f8f8;
margin: 0;
}
.category-section,
.subcategory-list-section {
margin-bottom: 30rpx;
}
.goods-grid {
display: flex;
flex-wrap: wrap;
padding: 20rpx;
gap: 20rpx;
}
.goods-item {
width: calc(50% - 10rpx);
padding: 10rpx;
box-sizing: border-box;
margin-bottom: 20rpx;
}
/* 其他样式保持不变... */
</style>
8.2 后台的代码如下:
#region 返回特定 大类ID之所有子类,并返回该子类下的产品信息
/// <summary>
/// 根据顶级分类ID获取商品数据
/// </summary>
[HttpPost]
public async Task<WebApiCallBack> GetCategoryGoods2025([FromBody] FMIntId entity)
{
var jm = new WebApiCallBack() { status = true };
try
{
int categoryId = entity.id;
// 0. 取得该分类信息
CoreCmsGoodsCategory currentTopCategory1 = await _goodsCategoryServices.QueryByIdAsync(categoryId);
if (currentTopCategory1 == null)
{
jm.status = false;
jm.msg = "分类不存在";
return jm;
}
// 将分类信息转换为 DTO
WxGoodCategoryDto currentTopCategory = new WxGoodCategoryDto
{
id = currentTopCategory1.id,
name = currentTopCategory1.name,
imageUrl = !string.IsNullOrEmpty(currentTopCategory1.imageUrl) ? currentTopCategory1.imageUrl : "/static/images/common/empty.png",
sort = currentTopCategory1.sort
};
// 1. 获取该分类的子分类
var subCategories = await _goodsCategoryServices.QueryListByClauseAsync(p => p.parentId == categoryId, p => p.sort, OrderByType.Asc);
if (subCategories.Any())
{
var childList = new List<WxGoodCategoryChild>();
foreach (var subCategory in subCategories)
{
childList.Add(new WxGoodCategoryChild()
{
id = subCategory.id,
name = subCategory.name,
imageUrl = !string.IsNullOrEmpty(subCategory.imageUrl) ? subCategory.imageUrl : "/static/images/common/empty.png",
sort = subCategory.sort
});
}
currentTopCategory.child = childList;
}
// 2. 创建分类商品详情
var currentCategoryGoodsDetail = new CategoryGoodsDetail
{
curentCategory = currentTopCategory
};
// 3. 获取该分类下的直接商品(使用与 GetAllCategories2025 相同的方法)
List<CoreGoodsInfo> topCategoryGoods = GetGoodInfoFromCategoryID(categoryId);
currentCategoryGoodsDetail.TopCategoryGoodsInfoList = topCategoryGoods;
currentCategoryGoodsDetail.subGoodsInfoList = new List<SubCategoryGoodsDetail>();
// 4. 获取子分类的商品信息
if (currentTopCategory.child != null && currentTopCategory.child.Count > 0)
{
foreach (var child in currentTopCategory.child)
{
var subCategoryGoodsDetailTemp = new SubCategoryGoodsDetail();
// 获取子分类的商品(使用与 GetAllCategories2025 相同的方法)
List<CoreGoodsInfo> subGoods = GetGoodInfoFromCategoryID(child.id);
subCategoryGoodsDetailTemp.GoodsInfoList = subGoods;
subCategoryGoodsDetailTemp.subCategory = new WxGoodCategoryDto
{
id = child.id,
name = child.name,
imageUrl = child.imageUrl,
sort = child.sort
};
subCategoryGoodsDetailTemp.expanded = false; // 默认不展开
currentCategoryGoodsDetail.subGoodsInfoList.Add(subCategoryGoodsDetailTemp);
}
}
jm.status = true;
jm.data = new CategoryData
{
wxGoodCategoryDto = null, // 这个接口不需要返回顶级分类列表
currentCategoryGoodsDetail = currentCategoryGoodsDetail
};
}
catch (Exception ex)
{
jm.status = false;
jm.msg = "获取分类商品失败:" + ex.Message;
}
return jm;
}
/// <summary>
/// 从分类ID获取商品信息(与 GetAllCategories2025 使用相同的方法)
/// </summary>
private List<CoreGoodsInfo> GetGoodInfoFromCategoryID(int categoryId)
{
// 使用与 GetAllCategories2025 相同的查询逻辑
var goodsList = _goodsServices.QueryPage(good => !good.isDel && good.goodsCategoryId == categoryId, "", 1, 18);
List<CoreGoodsInfo> result = new List<CoreGoodsInfo>();
if (goodsList != null && goodsList.Any())
{
foreach (var good in goodsList)
{
CoreGoodsInfo coreGoodsInfo = new CoreGoodsInfo();
coreGoodsInfo.id = good.id;
coreGoodsInfo.name = good.name;
coreGoodsInfo.price = good.price;
coreGoodsInfo.mktprice = good.mktprice;
coreGoodsInfo.image = good.image;
coreGoodsInfo.isRecommend = good.isRecommend;
coreGoodsInfo.isHot = good.isHot;
coreGoodsInfo.commentsCount = good.commentsCount;
coreGoodsInfo.buyCount = good.buyCount;
coreGoodsInfo.sort = good.sort;
result.Add(coreGoodsInfo);
}
}
return result;
}
#endregion
private List<CoreGoodsInfo> getGoodInfoFromCategoryID( int categoryId)
{
//2.1 大类之下的产品
var bigCategoryGoods =
_goodsServices.QueryPage(good => !good.isDel && good.goodsCategoryId == categoryId, "", 1, 18);
List<CoreGoodsInfo> bigGoods = new List<CoreGoodsInfo>();
//进行类型转换
if (bigCategoryGoods != null && bigCategoryGoods.Any())
{
foreach (var good in bigCategoryGoods)
{
CoreGoodsInfo coreGoodsInfo = new CoreGoodsInfo();
coreGoodsInfo.id = good.id;
coreGoodsInfo.name = good.name;
coreGoodsInfo.price = good.price;
coreGoodsInfo.mktprice = good.mktprice;
coreGoodsInfo.image = good.image;
coreGoodsInfo.isRecommend = good.isRecommend;
coreGoodsInfo.isHot = good.isHot;
coreGoodsInfo.commentsCount = good.commentsCount;
coreGoodsInfo.buyCount = good.buyCount;
coreGoodsInfo.sort = good.sort;
bigGoods.Add(coreGoodsInfo);
}
}
return bigGoods;
}
#region 获取所有商品分类栏目数据2025
/// <summary>
/// 获取所有商品分类栏目数据 ,并显示第一页产品信息
///
/// </summary>
/// <returns>
/// 1.一个大类列表,并指定一个当前类目(选一个为当前类目,选择条件为第一个非空类目,即默认第一个大类的类目是当前类目,
/// 如果第一个大类的类目下面产品是空的,就选择第二个类目为当前类目,
/// 如果第二个类目下面产品还是空的,就继续往下选择,
/// 如果全部类目下面都产品都是空的,还是选择第一个类目为当前类目)。
///2. 返回 "当前类目"之下的的小类列表和部分产品信息。
/// </returns>
[HttpPost]
public async Task<WebApiCallBack> GetAllCategories2025()
{
var jm = new WebApiCallBack() { status = true };
try
{
//1.取得所有的分类 ,并以父子关系进行分类
var data = await _goodsCategoryServices.QueryListByClauseAsync(p => p.isShow == true, p => p.sort,
OrderByType.Asc);
var wxGoodCategoryDto = new List<WxGoodCategoryDto>();//顶级分类
var parents = data.Where(p => p.parentId == 0).ToList();
if (parents.Any())
{
parents.ForEach(p =>
{
var model = new WxGoodCategoryDto();
model.id = p.id;
model.name = p.name;
model.imageUrl = !string.IsNullOrEmpty(p.imageUrl) ? p.imageUrl : "/static/images/common/empty.png";
model.sort = p.sort;
var childs = data.Where(o => o.parentId == model.id).ToList();
if (childs.Any())
{
var childsList = new List<WxGoodCategoryChild>();
childs.ForEach(o =>
{
childsList.Add(new WxGoodCategoryChild()
{
id = o.id,
imageUrl = !string.IsNullOrEmpty(o.imageUrl) ? o.imageUrl : "/static/images/common/empty.png",
name = o.name,
sort = o.sort
});
});
model.child = childsList;
}
// 下面这列就是 返回的 大类列表 (也就是顶级类目)
wxGoodCategoryDto.Add(model);
});
}
//2.取得第一个大类的产品信息
// 我们默认产品信息显示区,能显示18个产品,一行3个,6行。
CategoryGoodsDetail currentCategoryGoodsDetail = null;
if (wxGoodCategoryDto.Count > 0)
{
bool hasGoods = false; //当前类目下是否有产品
int currentCategoryIndex = 0;
do
{
//初始化 当前类目信息
currentCategoryGoodsDetail = new CategoryGoodsDetail();
int categoryId = wxGoodCategoryDto[currentCategoryIndex].id;
WxGoodCategoryDto currentTopCategory = wxGoodCategoryDto[currentCategoryIndex];
//2.1 大类之下的产品
List<CoreGoodsInfo> bigGoods = getGoodInfoFromCategoryID(categoryId);// new List<CoreGoodsInfo>();
if(bigGoods.Any())
{ hasGoods = true;
}
currentCategoryGoodsDetail.curentCategory = currentTopCategory;
currentCategoryGoodsDetail.TopCategoryGoodsInfoList = bigGoods;
currentCategoryGoodsDetail.subGoodsInfoList = new List<SubCategoryGoodsDetail>();
//2.2 取得大类下面的子类列表
if (currentTopCategory.child != null && currentTopCategory.child.Count > 0)
{
foreach (var child in currentTopCategory.child)
{
var subCategoryGoodsDetailTemp = new SubCategoryGoodsDetail();
// 获取子分类的商品
List<CoreGoodsInfo> subGoods = getGoodInfoFromCategoryID(child.id);
if (subGoods.Any()) hasGoods = true;
subCategoryGoodsDetailTemp.GoodsInfoList = subGoods;
subCategoryGoodsDetailTemp.subCategory = new WxGoodCategoryDto
{
id = child.id,
name = child.name,
imageUrl = child.imageUrl,
sort = child.sort
};
subCategoryGoodsDetailTemp.expanded = false; // 默认不展开
currentCategoryGoodsDetail.subGoodsInfoList.Add(subCategoryGoodsDetailTemp);
// 有子类也算有产品
if (subGoods.Any()) hasGoods = true;
}
}
// 如果当前分类有商品或者子分类有商品,就退出循环
if (hasGoods)
{
break;
}
currentCategoryIndex++;
}
while (currentCategoryIndex < wxGoodCategoryDto.Count);
// 如果所有分类都没有商品,仍然使用第一个分类
if (!hasGoods && wxGoodCategoryDto.Count > 0)
{
int firstCategoryId = wxGoodCategoryDto[0].id;
WxGoodCategoryDto firstTopCategory = wxGoodCategoryDto[0];
currentCategoryGoodsDetail = new CategoryGoodsDetail
{
curentCategory = firstTopCategory,
TopCategoryGoodsInfoList = new List<CoreGoodsInfo>(),
subGoodsInfoList = new List<SubCategoryGoodsDetail>()
};
// 添加子分类(即使没有商品)
if (firstTopCategory.child != null && firstTopCategory.child.Count > 0)
{
foreach (var child in firstTopCategory.child)
{
currentCategoryGoodsDetail.subGoodsInfoList.Add(new SubCategoryGoodsDetail
{
subCategory = new WxGoodCategoryDto
{
id = child.id,
name = child.name,
imageUrl = child.imageUrl,
sort = child.sort
},
GoodsInfoList = new List<CoreGoodsInfo>(),
expanded = false
});
}
}
}
}
jm.status = true;
jm.data = new CategoryData
{
wxGoodCategoryDto = wxGoodCategoryDto,
currentCategoryGoodsDetail = currentCategoryGoodsDetail
};
}
catch (Exception ex)
{
jm.status = false;
jm.msg = "获取分类数据失败:" + ex.Message;
}
return jm;
}
#endregion
修改之后运行代码界面如下

运行成功。教程完结。

被折叠的 条评论
为什么被折叠?



