chore:后台商品分类模板可以显示全部商品

This commit is contained in:
2026-09-15 10:51:50 +08:00
parent 782a26f446
commit 0925d28eb0
3 changed files with 220 additions and 165 deletions

View File

@@ -214,6 +214,10 @@ const localDevConfig = ({
uniacid: 3007,
domain: 'https://xcx.aigc-quickapp.com/',
},
'5g-317': { // 电磁兼容,模板显示不全商品
uniacid: 317,
domain: 'https://5g.aigc-quickapp.com/',
},
// })['5g-237']; // 商品分类,没有立即咨询文字
// })['5g-97']; // 大宗商品贸易,支持自定义字体大小、颜色
// })['5g-50']; // 燃气安全产品机器人,购物车无反应
@@ -229,7 +233,7 @@ const localDevConfig = ({
// })['local-11']; // 选择要使用的环境配置
// })['2811']; // 本地
})['1']; // 本地
})['5g-317']; // 本地
// })['local-2']; // 本地
// })['local-11']; // 本地
// })['1-test']; // test

View File

@@ -6,7 +6,7 @@
</view>
<block v-if="value.template == 99">
<view class="second-category-btns">
<scroll-view scroll-x="true" class="second-category-btns" :scroll-with-animation="true">
<view class="second-category-btn"
v-for="(one, oneIndex) in category.child_list"
:key="oneIndex"
@@ -14,7 +14,7 @@
@tap="selectSecondCategory(one)">
{{ one.category_name }}
</view>
</view>
</scroll-view>
<view class="category-goods-list" v-if="selectedSecondCategory">
<view class="goods-list show" :class="{ 'double-column': !isList, 'single-column': isList }" :data-template="value.template" v-if="secondCategoryGoodsList.length">
<view class="goods-item" v-for="(item, index) in secondCategoryGoodsList" :key="index">
@@ -554,6 +554,8 @@ export default {
goodsList: [],
pageIndex: 0,
pageSize: 10,
// 已加载完成的分类ID, 用于避免重复拉取同一分类的商品
_loadedGoodsKey: null,
totalPage: 1,
touchstartPosition: 0,
scrollType: 'top',
@@ -618,22 +620,19 @@ export default {
return this.value.template == 1 && this.value.level == 3;
},
/**
* 模板1三级分类: 只渲染"有商品"的二级分类, 过滤掉空分类
* 切换一级分类 / 装修刷新时, 重新计算过滤结果, 没商品的标题和空状态都不再显示
* 注意: 加载中(template1ChildLoading[id] === true)的二级分类,
* 暂视为"未确定" → 不计入过滤结果(等接口返回后再显示),
* 避免闪烁显示空标题
* 模板1三级分类: 渲染所有二级分类(包括无商品的空分类)
* 不再按"是否有商品"过滤, 保证一级分类下的二级分类全部显示,
* 避免 10 个二级分类只显示 8 个(2 个被当成空分类过滤掉)的问题
* 加载中(template1ChildLoading[id] === true)的二级分类暂不渲染, 避免闪烁
*/
filteredGroupedChildren() {
if (!this.isGroupedDisplay) return [];
if (!this.category || !this.category.child_list || !this.category.child_list.length) return [];
return this.category.child_list.filter(one => {
const loading = this.template1ChildLoading[one.category_id];
const list = this.template1ChildGoodsList[one.category_id];
// 还在加载中: 暂不渲染(避免闪烁显示空标题)
if (loading) return false;
// 加载完成且有商品: 显示; 没商品: 整块不渲染
return Array.isArray(list) && list.length > 0;
// 还在加载中: 暂不渲染(避免闪烁)
if (this.template1ChildLoading[one.category_id]) return false;
// 加载完成(无论是否有商品)都渲染, 无商品时显示空状态
return true;
});
},
/**
@@ -670,6 +669,9 @@ export default {
this.category.child_list.forEach(child => {
this.getTemplate1ChildGoods(child.category_id);
});
} else if (this.category.child_list && this.category.child_list.length) {
// 二级分类模式: 合并所有二级分类的商品, 避免只查一级分类ID时漏掉子分类商品
this.loadTemplate1Level2Goods();
} else {
this.getTemplate1Goods(this.category.category_id);
}
@@ -745,6 +747,9 @@ export default {
this.category.child_list.forEach(child => {
this.getTemplate1ChildGoods(child.category_id);
});
} else if (this.category.child_list && this.category.child_list.length) {
// 二级分类模式: 切换一级分类时重新合并所有二级分类商品
this.loadTemplate1Level2Goods();
} else {
this.getTemplate1Goods(this.category.category_id);
}
@@ -775,78 +780,162 @@ export default {
},
methods: {
/**
* 查询商品列表
* 拉取某分类下的全部商品(按后台实际数量)
* 后端 pageByCategory 存在分页上限, 这里自动翻页直到最后一页再合并返回,
* 保证分类模板(1/2/3/4/99)显示该分类下的全部商品, 而不是只显示第一页
* @param {Number} categoryId 分类ID
* @param {String} order 排序字段
* @param {String} sort 排序方向
* @returns {Promise<Array>}
*/
getGoodsList() {
if (this.type != 'goods' || this.loading || this.pageIndex >= this.totalPage) return;
getCategoryGoodsAll(categoryId, order = '', sort = '') {
return new Promise(resolve => {
const MAX_PAGE = 200; // 安全上限, 防止后台 page 参数失效时死循环
const all = [];
let prevLen = 0;
const fetchPage = page => {
if (page > MAX_PAGE) {
resolve(all);
return;
}
this.$api.sendRequest({
url: '/api/goodssku/pageByCategory',
data: {
category_id: categoryId,
page: page,
// 每次按较大 page_size 取, 减少请求次数; 实际每页条数由后台决定
page_size: 100,
order: order,
sort: sort
},
success: res => {
if (res.code == 0 && res.data && res.data.list && res.data.list.length) {
const list = res.data.list;
all.push(...list);
const pageCount = res.data.page_count;
// 后台返回了可靠的 page_count 时, 以它为准停止翻页
if (typeof pageCount === 'number' && pageCount >= 1 && page >= pageCount) {
resolve(all);
return;
}
// 自适应翻页: 不信任 page_count 时也按"上一页是否满页"判断
// 当前页条数 >= 上一页条数 → 可能还有下一页, 继续拉取
// 当前页比上一页少 → 已是末页(不满), 停止
if (page === 1 || list.length >= prevLen) {
prevLen = list.length;
fetchPage(page + 1);
} else {
resolve(all);
}
} else {
// 当前页无数据, 全部商品已取完
resolve(all);
}
},
fail: () => resolve(all)
});
};
fetchPage(1);
});
},
/**
* 拉取某分类及其所有二级子分类下的全部商品(自动翻页 + 合并去重)
* 解决后端 pageByCategory 只返回"直接"挂在当前分类的商品, 漏掉子分类商品的问题,
* 保证一级分类下的所有二级分类商品都能全部显示
* @param {Object} category 分类对象(含 child_list)
* @param {Number} categoryId 当前分类ID
* @param {String} order 排序字段
* @param {String} sort 排序方向
* @returns {Promise<Array>}
*/
getCategoryGoodsWithChildrenAll(category, categoryId, order = '', sort = '') {
const ids = [categoryId];
if (category && category.child_list && category.child_list.length) {
category.child_list.forEach(child => {
if (child.category_id) ids.push(child.category_id);
});
}
const promises = ids.map(id => this.getCategoryGoodsAll(id, order, sort));
return Promise.all(promises).then(lists => {
const all = [];
const seen = new Set();
lists.forEach(list => {
list.forEach(item => {
const key = item.sku_id || item.goods_id;
if (key && !seen.has(key)) {
seen.add(key);
all.push(item);
}
});
});
return all;
});
},
/**
* 查询商品列表(模板 2/3/4)
* 一次性拉取该分类下的全部商品, 不再依赖滚动分页
*/
getGoodsList() {
if (this.type != 'goods' || this.loading) return;
// 已加载过该分类时, 避免 scrolltolower 重复拉取
const categoryId = this.categoryId != -1 ? (this.value.template == 4 && this.currentOneCategory && this.currentOneCategory.child_list ? this.currentOneCategory.child_list[this.categoryId].category_id : this.category.child_list[this.categoryId].category_id)
: (this.value.template == 4 && this.currentOneCategory ? this.currentOneCategory.category_id : this.category.category_id);
if (this._loadedGoodsKey === categoryId) return;
this.loading = true;
this.pageIndex++;
if (this.$refs.loadingCover && this.pageIndex == 1 && this.loadType == 'part') {
setTimeout(() => {
this.$refs.loadingCover.show();
});
}
this.goodsList = [];
let order = '';
let sort = '';
if (this.value.sortWay) {
if (this.value.sortWay == 'default') {
// 综合排序
order = '';
sort = '';
} else if (this.value.sortWay == 'sales') {
// 销量排序
order = 'sale_num';
sort = 'desc';
} else if (this.value.sortWay == 'price') {
// 价格排序
order = 'discount_price';
sort = 'desc';
} else if (this.value.sortWay == 'news') {
// 上架时间排序
order = 'create_time';
sort = 'desc';
}
}
this.$api.sendRequest({
url: '/api/goodssku/pageByCategory',
data: {
page: this.pageIndex,
page_size: this.pageSize,
category_id: this.categoryId != -1 ? (this.value.template == 4 && this.currentOneCategory && this.currentOneCategory.child_list ? this.currentOneCategory.child_list[this.categoryId].category_id : this.category.child_list[this.categoryId].category_id)
: (this.value.template == 4 && this.currentOneCategory ? this.currentOneCategory.category_id : this.category.category_id),
order: order,
sort: sort
},
success: res => {
if (res.code == 0 && res.data) {
if (this.pageIndex == 1) this.goodsList = [];
//切换英文标题
if (uni.getStorageSync("lang") == 'en-us') {
// res.data.list.map(item => {
// item.goods_name = item.en_goods_name;
// return item;
// });
}
this.goodsList = this.goodsList.concat(res.data.list);
this.loadGoodsCartNum();
this.totalPage = res.data.page_count;
this.loading = false;
if (this.$refs.loadingCover && this.loadType == 'part') this.$refs.loadingCover.hide();
setTimeout(() => {
if (this.loadType == 'all') this.$emit('loadfinish', this.pageIndex == 1 ? this.index : -1);
const query = uni.createSelectorQuery().in(this);
query
.select('.goods-list')
.boundingClientRect(data => {
if (data && data.height < this.contentWrapHeight) this
.scrollType = 'none';
})
.exec();
}, 500);
}
}
if (this.$refs.loadingCover && this.loadType == 'part') {
setTimeout(() => {
this.$refs.loadingCover.show();
});
}
// 模板2/3/4: 未选中具体二级分类时, 后端 pageByCategory 只返回直接挂在一级分类的商品,
// 需合并其下所有二级子分类商品, 才能显示全; 选中具体二级分类时, 只查该分类
let goodsPromise;
if (this.value.template == 4 && this.categoryId == -1 && this.currentOneCategory && this.currentOneCategory.child_list && this.currentOneCategory.child_list.length) {
// 模板4: 当前一级分类在 currentOneCategory, 合并其下所有二级分类商品
goodsPromise = this.getCategoryGoodsWithChildrenAll(this.currentOneCategory, categoryId, order, sort);
} else if ((this.value.template == 2 || this.value.template == 3) && this.categoryId == -1 && this.category && this.category.child_list && this.category.child_list.length) {
goodsPromise = this.getCategoryGoodsWithChildrenAll(this.category, categoryId, order, sort);
} else {
goodsPromise = this.getCategoryGoodsAll(categoryId, order, sort);
}
goodsPromise.then(list => {
this.goodsList = list;
this._loadedGoodsKey = categoryId;
this.loadGoodsCartNum();
this.loading = false;
if (this.$refs.loadingCover && this.loadType == 'part') this.$refs.loadingCover.hide();
setTimeout(() => {
if (this.loadType == 'all') this.$emit('loadfinish', this.index);
const query = uni.createSelectorQuery().in(this);
query
.select('.goods-list')
.boundingClientRect(data => {
if (data && data.height < this.contentWrapHeight) this
.scrollType = 'none';
})
.exec();
}, 500);
});
},
/**
@@ -1218,23 +1307,12 @@ export default {
sort = this.value.sortType == 'desc' ? 'desc' : 'asc';
}
}
this.$api.sendRequest({
url: '/api/goodssku/pageByCategory',
data: {
category_id: categoryId,
page: 1,
page_size: this.pageSize,
order: order,
sort: sort
},
success: res => {
this.loading = false;
if (res.code == 0 && res.data && res.data.list) {
this.secondCategoryGoodsList = res.data.list;
} else {
this.secondCategoryGoodsList = [];
}
}
this.getCategoryGoodsAll(categoryId, order, sort).then(list => {
this.loading = false;
this.secondCategoryGoodsList = list;
}).catch(() => {
this.loading = false;
this.secondCategoryGoodsList = [];
});
},
/**
@@ -1254,25 +1332,38 @@ export default {
*/
getTemplate1Goods(categoryId) {
this.template1Loading = true;
this.$api.sendRequest({
url: '/api/goodssku/pageByCategory',
data: {
category_id: categoryId,
page: 1,
page_size: 10
},
success: res => {
this.template1Loading = false;
if (res.code == 0 && res.data && res.data.list) {
this.template1GoodsList = res.data.list;
} else {
this.template1GoodsList = [];
}
},
fail: () => {
this.template1Loading = false;
this.template1GoodsList = [];
}
this.getCategoryGoodsAll(categoryId).then(list => {
this.template1GoodsList = list;
this.template1Loading = false;
}).catch(() => {
this.template1GoodsList = [];
this.template1Loading = false;
});
},
/**
* 模板1二级分类模式: 加载当前一级分类下所有二级分类的商品并合并
* 避免只按一级分类ID查询时, 后端不返回子分类商品, 导致商品显示不全
*/
loadTemplate1Level2Goods() {
this.template1Loading = true;
const promises = this.category.child_list.map(child => this.getCategoryGoodsAll(child.category_id));
Promise.all(promises).then(lists => {
const all = [];
const seen = new Set();
lists.forEach(list => {
list.forEach(item => {
const key = item.sku_id || item.goods_id;
if (key && !seen.has(key)) {
seen.add(key);
all.push(item);
}
});
});
this.template1GoodsList = all;
this.template1Loading = false;
}).catch(() => {
this.template1GoodsList = [];
this.template1Loading = false;
});
},
/**
@@ -1282,25 +1373,12 @@ export default {
*/
getTemplate1ChildGoods(categoryId) {
this.$set(this.template1ChildLoading, categoryId, true);
this.$api.sendRequest({
url: '/api/goodssku/pageByCategory',
data: {
category_id: categoryId,
page: 1,
page_size: 10
},
success: res => {
this.$set(this.template1ChildLoading, categoryId, false);
if (res.code == 0 && res.data && res.data.list) {
this.$set(this.template1ChildGoodsList, categoryId, res.data.list);
} else {
this.$set(this.template1ChildGoodsList, categoryId, []);
}
},
fail: () => {
this.$set(this.template1ChildLoading, categoryId, false);
this.$set(this.template1ChildGoodsList, categoryId, []);
}
this.getCategoryGoodsAll(categoryId).then(list => {
this.$set(this.template1ChildLoading, categoryId, false);
this.$set(this.template1ChildGoodsList, categoryId, list);
}).catch(() => {
this.$set(this.template1ChildLoading, categoryId, false);
this.$set(this.template1ChildGoodsList, categoryId, []);
});
},
/**
@@ -2548,10 +2626,7 @@ export default {
// ===================== 模板99 商品分类样式 =====================
.second-category-btns {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: flex-start;
white-space: nowrap;
padding: 20rpx 20rpx 20rpx 24rpx;
height: 100rpx;
background: #fff;
@@ -2559,6 +2634,7 @@ export default {
box-sizing: border-box;
.second-category-btn {
display: inline-block;
padding: 10rpx 24rpx;
background: #f5f5f5;
color: #666;
@@ -2569,6 +2645,7 @@ export default {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
&.selected {
background: #FF4646;

View File

@@ -493,16 +493,16 @@ export default {
});
},
/**
* 全局查询所有有商品的二级分类, 按名称去重, 汇总成虚拟分类供 template99 渲染按钮
* 只要任何商品属于该二级分类, 按钮就显示; 切换一级分类时按钮不变(一直显示)
* 新增的二级分类只要有商品也会自动包含
* 汇总所有二级分类供 template99 渲染按钮(按名称去重)
* 注意: 保留"无商品"的二级分类也显示出来, 不再按是否有商品过滤,
* 避免一级分类下 10 个二级分类只显示 8 个(2 个被当成空分类过滤掉)的问题
*/
loadGlobalSecondCategoryBtns() {
if (!this.categoryTree || !this.categoryTree.length) {
this.template99Category = { category_name: '', child_list: [] };
return;
}
// 收集所有二级分类(保留原始顺序, 不去重, 查询所有同名后再去重)
// 收集所有二级分类(保留原始顺序, 不去重, 汇总后去重)
const allSecond = [];
this.categoryTree.forEach(one => {
if (one.child_list && one.child_list.length) {
@@ -515,45 +515,19 @@ export default {
this.template99Category = { category_name: '', child_list: [] };
return;
}
let pending = allSecond.length;
const results = [];
const finishOne = () => {
pending--;
if (pending <= 0) {
results.sort((a, b) => a._origIndex - b._origIndex);
// 按名称去重, 同名只保留第一个有商品的(只要任一同名有商品就显示)
const deduped = [];
const seenNames = {};
results.forEach(item => {
if (!seenNames[item.category_name]) {
seenNames[item.category_name] = true;
deduped.push(item);
}
});
this.template99Category = {
category_name: '',
child_list: deduped
};
// 按名称去重, 同名只保留第一个, 保持原始顺序
const deduped = [];
const seenNames = {};
allSecond.forEach(item => {
if (!seenNames[item.category_name]) {
seenNames[item.category_name] = true;
deduped.push(item);
}
};
allSecond.forEach(two => {
this.$api.sendRequest({
url: '/api/goodssku/pageByCategory',
data: {
category_id: two.category_id,
page: 1,
page_size: 1
},
success: res => {
if (res && res.code == 0 && res.data && res.data.list && res.data.list.length > 0) {
results.push(two);
}
},
complete: () => {
finishOne();
}
});
});
this.template99Category = {
category_name: '',
child_list: deduped
};
},
/**
* 切换一级分类