首页底部
Test Runner / hello (push) Successful in 4s

This commit is contained in:
2026-08-25 18:12:16 +08:00
parent 69734f36b4
commit 4bee0101e7
22 changed files with 378 additions and 183 deletions
+105 -84
View File
@@ -188,6 +188,16 @@ var _default = {
ProductGrid: ProductGrid,
FilterDrawer: FilterDrawer
},
watch: {
// 筛选标签增减 → 子组件会自测量并 emit,父组件这里不需要 watch
// 保留一个兜底:activeFilters.length 变化时主动触发一次重算
'activeFilters.length': function activeFiltersLength() {
this.recomputeScrollHeight();
},
'subCategories.length': function subCategoriesLength() {
this.recomputeScrollHeight();
}
},
data: function data() {
return {
searchKeyword: '',
@@ -213,21 +223,29 @@ var _default = {
products: [],
windowHeight: 667,
scrollTopVal: 0,
productScrollHeight: 400 // 商品滚动区高度(px),动态计算
productScrollHeight: 400,
// 商品滚动区高度(px),由子组件测量后动态计算
// 各子组件上报的高度(px),默认 0
_searchBarH: 0,
_countryBarH: 0,
_activeFiltersH: 0,
_subCategoriesH: 0
};
},
onLoad: function onLoad() {
var _this = this;
return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
var sysInfo;
var sysInfo, tabBarHeight;
return _regenerator.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
try {
sysInfo = uni.getSystemInfoSync(); // windowHeight 已自动扣除 uni-app 默认导航栏 & TabBar 高度
// 当前页是 navigationStyle:custom + TabBar 页,正好匹配可视区
_this.windowHeight = sysInfo.windowHeight || 667;
sysInfo = uni.getSystemInfoSync(); // tabBar 页 + navigationStyle:custom 时,windowHeight 在不同平台/版本可能不扣 tabBar
// 改用 screenHeight - tabBarHeight 计算,确保可视区高度正确
tabBarHeight = sysInfo.tabBarHeight || 50;
_this.windowHeight = (sysInfo.screenHeight || 750) - tabBarHeight;
console.log('[onLoad] screenHeight=' + sysInfo.screenHeight + ', tabBarHeight=' + tabBarHeight + ' → windowHeight=' + _this.windowHeight);
} catch (e) {
_this.windowHeight = 667;
}
@@ -238,10 +256,9 @@ var _default = {
_context.next = 6;
return _this.loadGoods(1);
case 6:
// 首屏数据渲染完成后计算滚动区高度
_this.$nextTick(function () {
_this.calcScrollHeight();
});
// 首屏数据渲染完成后,子组件会通过 mounted 自行测量并 emit 高度
// 这里兜底触发一次重算(防止某些端 mounted 比父组件 onLoad 晚)
_this.recomputeScrollHeight();
case 7:
case "end":
return _context.stop();
@@ -432,10 +449,8 @@ var _default = {
_this4.hasMore = _this4.products.length < total;
_this4.loadingMore = false;
// 数据变更后重算滚动区高度(筛选标签/子分类数量变化会影响上方占位高度
_this4.$nextTick(function () {
_this4.calcScrollHeight();
});
// 数据变更后兜底重算滚动区高度(子组件会自行 emit,这里兜底
_this4.recomputeScrollHeight();
console.log('[loadGoods] total=' + total + ', page=' + currentPage + ', items=' + products.length + ', loaded=' + _this4.products.length + ', hasMore=' + _this4.hasMore);
_context4.next = 25;
break;
@@ -542,64 +557,70 @@ var _default = {
});
},
/**
* 动态计算商品滚动区高度(px)。
* 用 createSelectorQuery 测量 SearchBar / CountryBar / ActiveFilters / subCategories 的实际高度
* 用 windowHeight 依次减去,得到 scroll-view 可用的固定高度。
* 子组件高度上报回调 —— 每个子组件在 mounted / 内容变化时自行测量 DOM 高度,
* 通过 @height-change 事件上报给父组件。父组件汇总后重算滚动区高度
*
* 这种方式解决了 uni-app 小程序端 createSelectorQuery 无法跨组件查询 DOM 的问题:
* H5 端 DOM 全局可查,但小程序每个组件有独立节点树,页面级 select 选不到子组件内部的节点。
*/
onSearchBarHeight: function onSearchBarHeight(h) {
this._searchBarH = h;
this.recomputeScrollHeight();
},
onCountryBarHeight: function onCountryBarHeight(h) {
this._countryBarH = h;
this.recomputeScrollHeight();
},
onActiveFiltersHeight: function onActiveFiltersHeight(h) {
this._activeFiltersH = h;
this.recomputeScrollHeight();
},
onSubCategoriesHeight: function onSubCategoriesHeight(h) {
this._subCategoriesH = h;
this.recomputeScrollHeight();
},
/**
* 根据 4 个子组件上报的高度,计算 scroll-view 可用的固定高度。
* —— 完全符合 uni-app / 微信小程序官方文档「scroll-y 需给固定 height」的要求。
*/
calcScrollHeight: function calcScrollHeight() {
var _this6 = this;
var query = uni.createSelectorQuery().in(this);
// 分别测量 4 个元素的 boundingClientRect
query.select('#searchBar').boundingClientRect();
query.select('#countryBar').boundingClientRect();
query.select('#activeFilters').boundingClientRect();
query.select('#subCategories').boundingClientRect();
query.exec(function (res) {
// res[0]=searchBar, res[1]=countryBar, res[2]=activeFilters, res[3]=subCategories
// 注意:v-if 为 false 时 res[i] 可能为 null
var searchBarH = res[0] ? res[0].height : 0;
var countryBarH = res[1] ? res[1].height : 0;
var activeFiltersH = res[2] ? res[2].height : 0;
var subCategoriesH = res[3] ? res[3].height : 0;
var totalUsed = searchBarH + countryBarH + activeFiltersH + subCategoriesH;
var h = _this6.windowHeight - totalUsed;
// 最小值保护:至少 200px,避免极端情况
if (h < 200) h = 200;
if (h > _this6.windowHeight) h = _this6.windowHeight;
_this6.productScrollHeight = Math.round(h);
console.log('[calcScrollHeight] windowHeight=' + _this6.windowHeight + ', searchBar=' + searchBarH + ', countryBar=' + countryBarH + ', activeFilters=' + activeFiltersH + ', subCategories=' + subCategoriesH + ' → scrollHeight=' + _this6.productScrollHeight);
});
recomputeScrollHeight: function recomputeScrollHeight() {
var totalUsed = this._searchBarH + this._countryBarH + this._activeFiltersH + this._subCategoriesH;
var h = this.windowHeight - totalUsed;
// 最小值保护:至少 200px,避免极端情况
if (h < 200) h = 200;
if (h > this.windowHeight) h = this.windowHeight;
this.productScrollHeight = Math.round(h);
console.log('[recomputeScrollHeight] windowHeight=' + this.windowHeight + ', searchBar=' + this._searchBarH + ', countryBar=' + this._countryBarH + ', activeFilters=' + this._activeFiltersH + ', subCategories=' + this._subCategoriesH + ' → scrollHeight=' + this.productScrollHeight);
},
selectCountry: function selectCountry(id) {
var _this7 = this;
var _this6 = this;
return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5() {
var country, filters;
return _regenerator.default.wrap(function _callee5$(_context5) {
while (1) {
switch (_context5.prev = _context5.next) {
case 0:
if (!(_this7.selectedCountry === id)) {
if (!(_this6.selectedCountry === id)) {
_context5.next = 8;
break;
}
_this7.selectedCountry = '';
_this7.activeFilters = _this7.activeFilters.filter(function (f) {
_this6.selectedCountry = '';
_this6.activeFilters = _this6.activeFilters.filter(function (f) {
return f.key !== 'country';
});
_context5.next = 5;
return _this7.loadCategoryData('');
return _this6.loadCategoryData('');
case 5:
_context5.next = 7;
return _this7.loadGoods(1);
return _this6.loadGoods(1);
case 7:
return _context5.abrupt("return");
case 8:
// 选中新国家
country = _this7.countries.find(function (c) {
country = _this6.countries.find(function (c) {
return c.id === id;
});
filters = _this7.activeFilters.filter(function (f) {
filters = _this6.activeFilters.filter(function (f) {
return f.key !== 'country';
});
if (country) {
@@ -608,13 +629,13 @@ var _default = {
label: country.name
});
}
_this7.selectedCountry = id;
_this7.activeFilters = filters;
_this6.selectedCountry = id;
_this6.activeFilters = filters;
_context5.next = 15;
return _this7.loadCategoryData(id);
return _this6.loadCategoryData(id);
case 15:
_context5.next = 17;
return _this7.loadGoods(1);
return _this6.loadGoods(1);
case 17:
case "end":
return _context5.stop();
@@ -624,16 +645,16 @@ var _default = {
}))();
},
selectCategory: function selectCategory(id) {
var _this8 = this;
var _this7 = this;
return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee6() {
return _regenerator.default.wrap(function _callee6$(_context6) {
while (1) {
switch (_context6.prev = _context6.next) {
case 0:
_this8.activeCategory = id;
_this8.loadSubCategories(id);
_this7.activeCategory = id;
_this7.loadSubCategories(id);
_context6.next = 4;
return _this8.loadGoods(1);
return _this7.loadGoods(1);
case 4:
case "end":
return _context6.stop();
@@ -643,15 +664,15 @@ var _default = {
}))();
},
selectSubCategory: function selectSubCategory(id) {
var _this9 = this;
var _this8 = this;
return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee7() {
return _regenerator.default.wrap(function _callee7$(_context7) {
while (1) {
switch (_context7.prev = _context7.next) {
case 0:
_this9.activeSubCategory = id;
_this8.activeSubCategory = id;
_context7.next = 3;
return _this9.loadGoods(1);
return _this8.loadGoods(1);
case 3:
case "end":
return _context7.stop();
@@ -683,27 +704,27 @@ var _default = {
this.filterGroups = groups;
},
removeFilter: function removeFilter(key) {
var _this10 = this;
var _this9 = this;
return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee8() {
var filters, groupId, groups;
return _regenerator.default.wrap(function _callee8$(_context8) {
while (1) {
switch (_context8.prev = _context8.next) {
case 0:
filters = _this10.activeFilters.filter(function (f) {
filters = _this9.activeFilters.filter(function (f) {
return f.key !== key;
});
if (!(key === 'country')) {
_context8.next = 9;
break;
}
_this10.selectedCountry = '';
_this10.activeFilters = filters;
_this9.selectedCountry = '';
_this9.activeFilters = filters;
_context8.next = 6;
return _this10.loadCategoryData('');
return _this9.loadCategoryData('');
case 6:
_context8.next = 8;
return _this10.loadGoods(1);
return _this9.loadGoods(1);
case 8:
return _context8.abrupt("return");
case 9:
@@ -712,7 +733,7 @@ var _default = {
break;
}
groupId = key.replace('tag_', '');
groups = _this10.filterGroups.map(function (g) {
groups = _this9.filterGroups.map(function (g) {
if (String(g.id) === String(groupId)) {
return Object.assign({}, g, {
selected: []
@@ -720,14 +741,14 @@ var _default = {
}
return g;
});
_this10.activeFilters = filters;
_this10.filterGroups = groups;
_this9.activeFilters = filters;
_this9.filterGroups = groups;
_context8.next = 16;
return _this10.loadGoods(1);
return _this9.loadGoods(1);
case 16:
return _context8.abrupt("return");
case 17:
_this10.activeFilters = filters;
_this9.activeFilters = filters;
case 18:
case "end":
return _context8.stop();
@@ -737,33 +758,33 @@ var _default = {
}))();
},
clearAllFilters: function clearAllFilters() {
var _this11 = this;
var _this10 = this;
return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee9() {
var hasCountry, groups;
return _regenerator.default.wrap(function _callee9$(_context9) {
while (1) {
switch (_context9.prev = _context9.next) {
case 0:
hasCountry = _this11.activeFilters.some(function (f) {
hasCountry = _this10.activeFilters.some(function (f) {
return f.key === 'country';
});
_this11.activeFilters = [];
if (!(hasCountry || _this11.selectedCountry)) {
_this10.activeFilters = [];
if (!(hasCountry || _this10.selectedCountry)) {
_context9.next = 6;
break;
}
_this11.selectedCountry = '';
_this10.selectedCountry = '';
_context9.next = 6;
return _this11.loadCategoryData('');
return _this10.loadCategoryData('');
case 6:
groups = _this11.filterGroups.map(function (g) {
groups = _this10.filterGroups.map(function (g) {
return Object.assign({}, g, {
selected: []
});
});
_this11.filterGroups = groups;
_this10.filterGroups = groups;
_context9.next = 10;
return _this11.loadGoods(1);
return _this10.loadGoods(1);
case 10:
case "end":
return _context9.stop();
@@ -781,7 +802,7 @@ var _default = {
this.filterGroups = groups;
},
confirmFilters: function confirmFilters() {
var _this12 = this;
var _this11 = this;
return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee10() {
var filters, countryFilter;
return _regenerator.default.wrap(function _callee10$(_context10) {
@@ -789,11 +810,11 @@ var _default = {
switch (_context10.prev = _context10.next) {
case 0:
filters = [];
countryFilter = _this12.activeFilters.find(function (f) {
countryFilter = _this11.activeFilters.find(function (f) {
return f.key === 'country';
});
if (countryFilter) filters.push(countryFilter);
_this12.filterGroups.forEach(function (group) {
_this11.filterGroups.forEach(function (group) {
if (group.selected.length > 0) {
var labels = group.tags.filter(function (t) {
return group.selected.indexOf(t.id) > -1;
@@ -808,10 +829,10 @@ var _default = {
}
}
});
_this12.activeFilters = filters;
_this12.showFilter = false;
_this11.activeFilters = filters;
_this11.showFilter = false;
_context10.next = 8;
return _this12.loadGoods(1);
return _this11.loadGoods(1);
case 8:
case "end":
return _context10.stop();
+1 -1
View File
@@ -1 +1 @@
<view class="products-page" style="{{'height:'+(windowHeight+'px')+';'}}"><search-bar vue-id="08007ac0-1" keyword="{{searchKeyword}}" data-event-opts="{{[['^input',[['onSearchInput']]],['^blur',[['onSearchBlur']]],['^filter',[['toggleFilter']]]]}}" bind:input="__e" bind:blur="__e" bind:filter="__e" bind:__l="__l"></search-bar><country-bar vue-id="08007ac0-2" list="{{countries}}" selected-id="{{selectedCountry}}" data-event-opts="{{[['^select',[['selectCountry']]]]}}" bind:select="__e" bind:__l="__l"></country-bar><view class="content-body"><category-sidebar vue-id="08007ac0-3" list="{{categories}}" active-id="{{activeCategory}}" data-event-opts="{{[['^select',[['selectCategory']]]]}}" bind:select="__e" bind:__l="__l"></category-sidebar><view class="right-wrap"><active-filters vue-id="08007ac0-4" list="{{activeFilters}}" data-event-opts="{{[['^remove',[['removeFilter']]],['^clearAll',[['clearAllFilters']]]]}}" bind:remove="__e" bind:clearAll="__e" bind:__l="__l"></active-filters><product-grid vue-id="08007ac0-5" list="{{products}}" sub-categories="{{subCategories}}" active-sub-id="{{activeSubCategory}}" has-more="{{hasMore}}" loading="{{loadingMore}}" scroll-top-val="{{scrollTopVal}}" scroll-height="{{productScrollHeight}}" data-event-opts="{{[['^subSelect',[['selectSubCategory']]],['^productClick',[['goDetail']]],['^scrollToLower',[['onScrollToLower']]]]}}" bind:subSelect="__e" bind:productClick="__e" bind:scrollToLower="__e" bind:__l="__l"></product-grid></view></view><filter-drawer vue-id="08007ac0-6" visible="{{showFilter}}" groups="{{filterGroups}}" data-event-opts="{{[['^close',[['closeFilter']]],['^toggleTag',[['toggleFilterTag']]],['^reset',[['resetFilters']]],['^confirm',[['confirmFilters']]]]}}" bind:close="__e" bind:toggleTag="__e" bind:reset="__e" bind:confirm="__e" bind:__l="__l"></filter-drawer></view>
<view class="products-page" style="{{'height:'+(windowHeight+'px')+';'}}"><search-bar vue-id="08007ac0-1" keyword="{{searchKeyword}}" data-event-opts="{{[['^input',[['onSearchInput']]],['^blur',[['onSearchBlur']]],['^filter',[['toggleFilter']]],['^heightChange',[['onSearchBarHeight']]]]}}" bind:input="__e" bind:blur="__e" bind:filter="__e" bind:heightChange="__e" bind:__l="__l"></search-bar><country-bar vue-id="08007ac0-2" list="{{countries}}" selected-id="{{selectedCountry}}" data-event-opts="{{[['^select',[['selectCountry']]],['^heightChange',[['onCountryBarHeight']]]]}}" bind:select="__e" bind:heightChange="__e" bind:__l="__l"></country-bar><view class="content-body"><category-sidebar vue-id="08007ac0-3" list="{{categories}}" active-id="{{activeCategory}}" data-event-opts="{{[['^select',[['selectCategory']]]]}}" bind:select="__e" bind:__l="__l"></category-sidebar><view class="right-wrap"><active-filters vue-id="08007ac0-4" list="{{activeFilters}}" data-event-opts="{{[['^remove',[['removeFilter']]],['^clearAll',[['clearAllFilters']]],['^heightChange',[['onActiveFiltersHeight']]]]}}" bind:remove="__e" bind:clearAll="__e" bind:heightChange="__e" bind:__l="__l"></active-filters><product-grid vue-id="08007ac0-5" list="{{products}}" sub-categories="{{subCategories}}" active-sub-id="{{activeSubCategory}}" has-more="{{hasMore}}" loading="{{loadingMore}}" scroll-top-val="{{scrollTopVal}}" scroll-height="{{productScrollHeight}}" data-event-opts="{{[['^subSelect',[['selectSubCategory']]],['^productClick',[['goDetail']]],['^scrollToLower',[['onScrollToLower']]],['^subHeightChange',[['onSubCategoriesHeight']]]]}}" bind:subSelect="__e" bind:productClick="__e" bind:scrollToLower="__e" bind:subHeightChange="__e" bind:__l="__l"></product-grid></view></view><filter-drawer vue-id="08007ac0-6" visible="{{showFilter}}" groups="{{filterGroups}}" data-event-opts="{{[['^close',[['closeFilter']]],['^toggleTag',[['toggleFilterTag']]],['^reset',[['resetFilters']]],['^confirm',[['confirmFilters']]]]}}" bind:close="__e" bind:toggleTag="__e" bind:reset="__e" bind:confirm="__e" bind:__l="__l"></filter-drawer></view>