首页底部
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
+69 -41
View File
@@ -7,6 +7,7 @@
@input="onSearchInput"
@blur="onSearchBlur"
@filter="toggleFilter"
@height-change="onSearchBarHeight"
/>
<!-- 国家选择条 -->
@@ -14,6 +15,7 @@
:list="countries"
:selected-id="selectedCountry"
@select="selectCountry"
@height-change="onCountryBarHeight"
/>
<!-- 主体内容左侧分类侧边栏(固定宽度) + 右侧内容区(自动剩余宽度) -->
@@ -32,6 +34,7 @@
:list="activeFilters"
@remove="removeFilter"
@clear-all="clearAllFilters"
@height-change="onActiveFiltersHeight"
/>
<!-- 产品列表(内含子分类条件+滚动商品) -->
@@ -46,6 +49,7 @@
@sub-select="selectSubCategory"
@product-click="goDetail"
@scroll-to-lower="onScrollToLower"
@sub-height-change="onSubCategoriesHeight"
/>
</view>
</view>
@@ -83,6 +87,16 @@ export default {
ProductGrid,
FilterDrawer
},
watch: {
// 筛选标签增减 → 子组件会自测量并 emit,父组件这里不需要 watch
// 保留一个兜底:activeFilters.length 变化时主动触发一次重算
'activeFilters.length'() {
this.recomputeScrollHeight()
},
'subCategories.length'() {
this.recomputeScrollHeight()
}
},
data() {
return {
searchKeyword: '',
@@ -110,25 +124,33 @@ export default {
products: [],
windowHeight: 667,
scrollTopVal: 0,
productScrollHeight: 400 // 商品滚动区高度(px),动态计算
productScrollHeight: 400, // 商品滚动区高度(px),由子组件测量后动态计算
// 各子组件上报的高度(px),默认 0
_searchBarH: 0,
_countryBarH: 0,
_activeFiltersH: 0,
_subCategoriesH: 0
}
},
async onLoad() {
try {
const sysInfo = uni.getSystemInfoSync()
// windowHeight 已自动扣除 uni-app 默认导航栏 & TabBar 高度
// 当前页是 navigationStyle:custom + TabBar 页,正好匹配可视区
this.windowHeight = sysInfo.windowHeight || 667
// tabBar 页 + navigationStyle:custom 时,windowHeight 在不同平台/版本可能不扣 tabBar
// 改用 screenHeight - tabBarHeight 计算,确保可视区高度正确
const 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
}
await this.initData()
this.loadSubCategories('all')
await this.loadGoods(1)
// 首屏数据渲染完成后计算滚动区高度
this.$nextTick(() => {
this.calcScrollHeight()
})
// 首屏数据渲染完成后,子组件会通过 mounted 自行测量并 emit 高度
// 这里兜底触发一次重算(防止某些端 mounted 比父组件 onLoad 晚)
this.recomputeScrollHeight()
},
methods: {
async initData() {
@@ -259,10 +281,8 @@ export default {
this.hasMore = this.products.length < total
this.loadingMore = false
// 数据变更后重算滚动区高度(筛选标签/子分类数量变化会影响上方占位高度
this.$nextTick(() => {
this.calcScrollHeight()
})
// 数据变更后兜底重算滚动区高度(子组件会自行 emit,这里兜底
this.recomputeScrollHeight()
console.log(
'[loadGoods] total=' + total +
@@ -364,38 +384,46 @@ export default {
},
/**
* 动态计算商品滚动区高度(px)。
* 用 createSelectorQuery 测量 SearchBar / CountryBar / ActiveFilters / subCategories 的实际高度
* 用 windowHeight 依次减去,得到 scroll-view 可用的固定高度。
* 子组件高度上报回调 —— 每个子组件在 mounted / 内容变化时自行测量 DOM 高度,
* 通过 @height-change 事件上报给父组件。父组件汇总后重算滚动区高度
*
* 这种方式解决了 uni-app 小程序端 createSelectorQuery 无法跨组件查询 DOM 的问题:
* H5 端 DOM 全局可查,但小程序每个组件有独立节点树,页面级 select 选不到子组件内部的节点。
*/
onSearchBarHeight(h) {
this._searchBarH = h
this.recomputeScrollHeight()
},
onCountryBarHeight(h) {
this._countryBarH = h
this.recomputeScrollHeight()
},
onActiveFiltersHeight(h) {
this._activeFiltersH = h
this.recomputeScrollHeight()
},
onSubCategoriesHeight(h) {
this._subCategoriesH = h
this.recomputeScrollHeight()
},
/**
* 根据 4 个子组件上报的高度,计算 scroll-view 可用的固定高度。
* —— 完全符合 uni-app / 微信小程序官方文档「scroll-y 需给固定 height」的要求。
*/
calcScrollHeight() {
const 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((res) => {
// res[0]=searchBar, res[1]=countryBar, res[2]=activeFilters, res[3]=subCategories
// 注意:v-if 为 false 时 res[i] 可能为 null
const searchBarH = res[0] ? res[0].height : 0
const countryBarH = res[1] ? res[1].height : 0
const activeFiltersH = res[2] ? res[2].height : 0
const subCategoriesH = res[3] ? res[3].height : 0
recomputeScrollHeight() {
const totalUsed = this._searchBarH + this._countryBarH +
this._activeFiltersH + this._subCategoriesH
let h = this.windowHeight - totalUsed
// 最小值保护:至少 200px,避免极端情况
if (h < 200) h = 200
if (h > this.windowHeight) h = this.windowHeight
const totalUsed = searchBarH + countryBarH + activeFiltersH + subCategoriesH
let h = this.windowHeight - totalUsed
// 最小值保护:至少 200px,避免极端情况
if (h < 200) h = 200
if (h > this.windowHeight) h = this.windowHeight
this.productScrollHeight = Math.round(h)
console.log('[calcScrollHeight] windowHeight=' + this.windowHeight +
', searchBar=' + searchBarH + ', countryBar=' + countryBarH +
', activeFilters=' + activeFiltersH + ', subCategories=' + subCategoriesH +
' → scrollHeight=' + this.productScrollHeight)
})
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)
},
async selectCountry(id) {