refactor: 统一重构底部导航栏间距逻辑与显示规则

1.  新增全局计算属性统一处理底部导航栏显示判断与间距样式
2.  替换多个页面的手写底部间距逻辑为通用样式
3.  新增路由匹配工具函数,支持灵活配置导航栏显示范围
4.  优化底部导航组件的显示条件判断
This commit is contained in:
2026-06-16 11:25:54 +08:00
parent 6c6822ca16
commit 64d3ac8f8a
18 changed files with 141 additions and 108 deletions

View File

@@ -22,6 +22,57 @@ export default {
tabBarList() {
return this.$store.state.tabBarList;
},
shouldShowTabBar() {
const tabBarList = this.$store.state.tabBarList;
if (!tabBarList || !tabBarList.list) {
return false;
}
const mode = tabBarList.page_visibility_mode;
const routeList = tabBarList.page_routes || [];
if (!mode || mode === 'all') {
return true;
}
if (mode === 'none') {
return false;
}
const pages = getCurrentPages();
if (pages.length === 0) {
return true;
}
const currentPage = pages[pages.length - 1];
let currentRoute = '';
if (currentPage.route) {
currentRoute = currentPage.route;
} else if (currentPage.$page && currentPage.$page.path) {
currentRoute = currentPage.$page.path;
}
if (!currentRoute) {
return true;
}
if (!currentRoute.startsWith('/')) {
currentRoute = '/' + currentRoute;
}
const inList = this.$util && this.$util.isPageInRouteList
? this.$util.isPageInRouteList(currentRoute, routeList)
: false;
if (mode === 'whitelist') {
return inList;
}
if (mode === 'blacklist') {
return !inList;
}
return true;
},
tabBarPagePaddingStyle() {
return this.$util.objToStyle({
paddingBottom: this.shouldShowTabBar ? '112rpx' : '0'
});
},
tabBarPaddingStyleValue() {
return {
paddingBottom: this.shouldShowTabBar ? '112rpx' : '0'
};
},
siteInfo() {
return this.$store.state.siteInfo;
},

View File

@@ -152,6 +152,66 @@ export const resolvePageUrl = (page_key, params = {}) => {
return url;
};
/**
* 判断当前路由是否命中 page_routes 列表
* 支持 3 种写法,可混合使用:
* 1) PAGE_KEY_MAP 的 key → 如 "PAGE_INDEX"
* 2) 实际页面路径 → 如 "/pages/index/index"
* 3) 通配符路径 → 如 "/pages_tool/*" 或 "PAGE_ORDER_*"
* @param {string} currentRoute 当前页面路径(会自动补 / 前缀)
* @param {string[]} routeList 后台配置的 page_routes 数组
* @returns {boolean} 是否命中
*/
export const isPageInRouteList = (currentRoute, routeList) => {
if (!currentRoute || !routeList || !Array.isArray(routeList) || routeList.length === 0) {
return false;
}
const route = currentRoute.startsWith('/') ? currentRoute : '/' + currentRoute;
for (const item of routeList) {
if (!item || typeof item !== 'string') continue;
const entry = item.trim();
if (!entry) continue;
if (entry.endsWith('/*')) {
const prefix = entry.slice(0, -1);
if (route.startsWith(prefix)) return true;
continue;
}
if (entry.endsWith('_*') && PAGE_KEY_MAP) {
const keyPrefix = entry.slice(0, -1);
const matchedPaths = [];
for (const k of Object.keys(PAGE_KEY_MAP)) {
if (k.startsWith(keyPrefix) && PAGE_KEY_MAP[k]) {
matchedPaths.push(PAGE_KEY_MAP[k]);
}
}
for (const p of matchedPaths) {
const pn = p.startsWith('/') ? p : '/' + p;
const dirPrefix = pn.substring(0, pn.lastIndexOf('/') + 1);
if (route.startsWith(dirPrefix)) return true;
if (route === pn) return true;
}
continue;
}
if (PAGE_KEY_MAP && PAGE_KEY_MAP[entry]) {
let mapped = PAGE_KEY_MAP[entry];
mapped = mapped.startsWith('/') ? mapped : '/' + mapped;
if (route === mapped) return true;
continue;
}
if (entry.startsWith('/')) {
if (route === entry) return true;
continue;
}
}
return false;
};
/**
* 解析 link 对象得到最终可跳转的 URL
* 优先级: page_key → wap_url → null
@@ -343,6 +403,9 @@ export default {
/** 根据 page_key + params 解析实际URL */
resolvePageUrl,
/** 判断当前路由是否命中 page_routes 列表(支持 PAGE_KEY / 实际路径 / 通配符) */
isPageInRouteList,
/** 解析 link 对象得到最终可跳转的 URL(优先级: page_key → wap_url) */
resolveLinkUrl,

View File

@@ -1,5 +1,5 @@
<template>
<view data-component-name="diy-bottom-nav" v-if="tabBarList && tabBarList.list">
<view data-component-name="diy-bottom-nav" v-if="shouldShowTabBar">
<view class="tab-bar" :style="{ backgroundColor: tabBarList.backgroundColor }">
<view class="tabbar-border"></view>
<view class="item" v-for="(item, index) in tabBarList.list" :key="item.id" @tap.stop="redirectTo(item.link)">
@@ -110,7 +110,7 @@ export default {
},
packageEntryTab() {
return this.$store.state.packageEntryTab;
}
},
},
watch: {
cartChange: function (nval, oval) {

View File

@@ -5,15 +5,15 @@
<view :class="['list-wrap', value.style]" :style="warpCss">
<view :class="['item', value.ornament.type]" v-for="(item, index) in list" :key="index"
:style="itemCss" @tap.stop="handlerClick(item)">
<view class="merch-img">
<view class="merch-img" @tap.stop="handlerClick(item)">
<image class="cover-img" :src="$util.img(item.merch_image)" mode="widthFix"
@error="imgError(index)" show-menu-by-longpress />
</view>
<view class="info-wrap">
<text class="title">{{ item.merch_name }}</text>
<text class="desc">{{ item.desc }}</text>
<view class="read-wrap"></view>
</view>
<text class="title" @tap.stop="handlerClick(item)">{{ item.merch_name }}</text>
<text class="desc">{{ item.desc }}</text>
<view class="read-wrap"></view>
</view>
</view>
</view>
</view>
@@ -27,14 +27,16 @@
<view class="merch-nav-item graphic" v-for="(item, index) in list" :key="index"
:style="{ width: 100 / 4 + '%' }" @tap.stop="handlerClick(item)">
<view class="graphic-img" v-if="value.mode != 'text'"
:style="{ fontSize: value.imageSize * 2 + 'rpx', width: value.imageSize * 2 + 'rpx', height: value.imageSize * 2 + 'rpx' }">
:style="{ fontSize: value.imageSize * 2 + 'rpx', width: value.imageSize * 2 + 'rpx', height: value.imageSize * 2 + 'rpx' }"
@tap.stop="handlerClick(item)">
<image
:src="$util.img(item.merch_image) || $util.img('public/uniapp/default_img/goods.png')"
mode="aspectFill" show-menu-by-longpress
style="max-width: 80rpx; max-height: 80rpx; border-radius: 50rpx;" />
</view>
<text class="graphic-text"
style="font-size: 28rpx; font-weight: normal; color: rgb(48, 49, 51);">
style="font-size: 28rpx; font-weight: normal; color: rgb(48, 49, 51);"
@tap.stop="handlerClick(item)">
{{ item.merch_name }}
</text>
</view>

View File

@@ -18,10 +18,10 @@
</view>
<!-- #ifdef MP -->
<mescroll-uni ref="mescroll" @getData="getListData" top="176rpx" v-if="storeToken" :style="orderListPageStyle">
<mescroll-uni ref="mescroll" @getData="getListData" top="176rpx" v-if="storeToken" :style="tabBarPagePaddingStyle">
<!-- #endif -->
<!-- #ifndef MP -->
<mescroll-uni ref="mescroll" @getData="getListData" top="196rpx" v-if="storeToken" :style="orderListPageStyle">
<mescroll-uni ref="mescroll" @getData="getListData" top="196rpx" v-if="storeToken" :style="tabBarPagePaddingStyle">
<!-- #endif -->
<block slot="list">
<view class="order-list" v-if="orderList.length > 0">
@@ -448,11 +448,6 @@ export default {
mpOrderList() {
if (!this.orderList[this.status]) return;
return this.orderList[this.status].list || [];
},
orderListPageStyle() {
return this.$util.objToStyle({
paddingBottom: this.tabBarList && this.tabBarList.list ? '112rpx' : '0'
});
}
},
watch: {

View File

@@ -1,6 +1,6 @@
<template>
<view class="content" :style="themeColor">
<mescroll-uni ref="mescroll" @getData="getGoodsList" :style="fenxiaoGoodsListPageStyle">
<mescroll-uni ref="mescroll" @getData="getGoodsList" :style="tabBarPagePaddingStyle">
<block slot="list">
<view class="goods-list"
:style="{ backgroundImage: 'url(' + $util.img('public/uniapp/fenxiao/promote/promote_bg.png') + ')' }">
@@ -139,14 +139,7 @@ import list from './public/js/goods_list.js';
import fenxiaoWords from 'common/js/fenxiao-words.js';
export default {
mixins: [list, fenxiaoWords],
computed: {
fenxiaoGoodsListPageStyle() {
return this.$util.objToStyle({
paddingBottom: this.tabBarList && this.tabBarList.list ? '112rpx' : '0'
});
}
}
mixins: [list, fenxiaoWords]
};
</script>

View File

@@ -581,7 +581,7 @@ export default {
},
fenxiaoIndexPageStyle() {
const style = {
paddingBottom: this.tabBarList && this.tabBarList.list ? '112rpx' : '0'
...this.tabBarPaddingStyleValue
};
if (this.$util && this.$util.img) {
style.background = 'url(' + this.$util.img('public/uniapp/fenxiao/index/commissionFission-background.png') + ')';

View File

@@ -7,7 +7,7 @@
</view>
</block>
</view>
<mescroll-uni ref="mescroll" @getData="getData" top="90" class="member-point" :size="8" v-if="storeToken" :style="fenxiaoOrderPageStyle">
<mescroll-uni ref="mescroll" @getData="getData" top="90" class="member-point" :size="8" v-if="storeToken" :style="tabBarPagePaddingStyle">
<view class="goods_list" slot="list">
<view class="order-list">
<view class="order-item" v-for="(orderItem, orderIndex) in orderList" :key="orderIndex"
@@ -134,13 +134,6 @@ export default {
subMemberId: ''
};
},
computed: {
fenxiaoOrderPageStyle() {
return this.$util.objToStyle({
paddingBottom: this.tabBarList && this.tabBarList.list ? '112rpx' : '0'
});
}
},
mixins: [fenxiaoWords],
onLoad(option) {
if (option.type != undefined) this.selectId = option.type;

View File

@@ -96,10 +96,9 @@
},
computed: {
categoryPageStyle() {
const hasTabBar = this.tabBarList && this.tabBarList.list;
return this.$util.objToStyle({
height: 'calc(100vh - 56px)',
paddingBottom: hasTabBar ? '112rpx' : '0'
...this.tabBarPaddingStyleValue
});
}
},

View File

@@ -44,7 +44,7 @@
</view>
</view>
<mescroll-uni top="180" @getData="getData" ref="mescroll" :size="10" :style="pointGoodsListPageStyle">
<mescroll-uni top="180" @getData="getData" ref="mescroll" :size="10" :style="tabBarPagePaddingStyle">
<block slot="list">
<view class="list-wrap">
<view class="goods-list double-column" v-if="goodsList.length">
@@ -164,13 +164,6 @@ export default {
point: 0,
};
},
computed: {
pointGoodsListPageStyle() {
return this.$util.objToStyle({
paddingBottom: this.tabBarList && this.tabBarList.list ? '112rpx' : '0'
});
}
},
onLoad(options) {
this.categoryId = options.category_id || 0;
this.keyword = options.keyword || '';

View File

@@ -22,7 +22,7 @@
<view class="point-navbar-block" :style="{ height: menuButtonBounding.bottom + 'px' }"></view>
<!-- #endif -->
<scroll-view scroll-y="true" class="point-scroll-view" @scrolltolower="getData" :style="pointListPageStyle">
<scroll-view scroll-y="true" class="point-scroll-view" @scrolltolower="getData" :style="tabBarPagePaddingStyle">
<view class="point-wrap" :style="{ 'background-position-y': -menuButtonBounding.bottom + 'px' }">
<view class="head-box">
<view class="account-content">
@@ -298,13 +298,6 @@ export default {
newestGame: null
};
},
computed: {
pointListPageStyle() {
return this.$util.objToStyle({
paddingBottom: this.tabBarList && this.tabBarList.list ? '112rpx' : '0'
});
}
},
onLoad(option) {
setTimeout(() => {
if (!this.addonIsExist.pointexchange) {

View File

@@ -1,6 +1,6 @@
<template>
<view :style="themeColor">
<mescroll-uni @getData="getData" ref="mescroll" :style="articleListPageStyle">
<mescroll-uni @getData="getData" ref="mescroll" :style="tabBarPagePaddingStyle">
<block slot="list">
<view class="article-wrap" v-if="list.length">
<ns-adv keyword="NS_ARTICLE" class-name="adv-wrap"></ns-adv>
@@ -43,13 +43,6 @@ export default {
list: []
};
},
computed: {
articleListPageStyle() {
return this.$util.objToStyle({
paddingBottom: this.tabBarList && this.tabBarList.list ? '112rpx' : '0'
});
}
},
onShow() {
this.setPublicShare();
},

View File

@@ -1,6 +1,6 @@
<template>
<view>
<view class="about w100" :style="casesPageStyle">
<view class="about w100" :style="tabBarPagePaddingStyle">
<view class="list_cotact padding-top">
<view class="container">
<block v-for="(item, index) in dataList" :key="index">
@@ -109,13 +109,6 @@ export default {
video_url: ""
};
},
computed: {
casesPageStyle() {
return this.$util.objToStyle({
paddingBottom: this.tabBarList && this.tabBarList.list ? '112rpx' : '0'
});
}
},
onLoad() {
this.init();
},

View File

@@ -7,7 +7,7 @@
<text class="iconfont icon-sousuo3" @tap="search"></text>
</view>
</view>
<mescroll-uni ref="mescroll" top="100" @getData="getData" :style="fileListPageStyle">
<mescroll-uni ref="mescroll" top="100" @getData="getData" :style="tabBarPagePaddingStyle">
<view slot="list">
<block v-if="list.length > 0">
<view class="article-wrap">
@@ -54,13 +54,6 @@ export default {
keyword: ''
}
},
computed: {
fileListPageStyle() {
return this.$util.objToStyle({
paddingBottom: this.tabBarList && this.tabBarList.list ? '112rpx' : '0'
});
}
},
onLoad(options) {
if (options.category_id > 0) {
this.category_id = options.category_id

View File

@@ -1,6 +1,6 @@
<template>
<view :data-theme="themeStyle">
<mescroll-uni @getData="getBrandList" ref="mescroll" size="20" :style="brandPageStyle">
<mescroll-uni @getData="getBrandList" ref="mescroll" size="20" :style="tabBarPagePaddingStyle">
<block slot="list">
<ns-adv keyword="NS_BRAND" class-name="adv-wrap"></ns-adv>
<view class="brand-content" v-if="brandList.length > 0">
@@ -34,13 +34,6 @@ export default {
siteId: 0
};
},
computed: {
brandPageStyle() {
return this.$util.objToStyle({
paddingBottom: this.tabBarList && this.tabBarList.list ? '112rpx' : '0'
});
}
},
onLoad(options) {
if (options.site_id) this.siteId = options.site_id;
},

View File

@@ -12,7 +12,7 @@
</view>
</view>
<mescroll-uni ref="mescroll" top="100" @getData="getMemberCouponList" :style="couponListPageStyle">
<mescroll-uni ref="mescroll" top="100" @getData="getMemberCouponList" :style="tabBarPagePaddingStyle">
<block slot="list">
<view class="coupon-listone">
<view class="item" v-for="(item, index) in list" :key="index" @click="liClick(item, index)"
@@ -95,13 +95,6 @@ export default {
mpShareData: null //小程序分享数据
};
},
computed: {
couponListPageStyle() {
return this.$util.objToStyle({
paddingBottom: this.tabBarList && this.tabBarList.list ? '112rpx' : '0'
});
}
},
onLoad(option) {
setTimeout(() => {
if (!this.addonIsExist.coupon) {

View File

@@ -1,6 +1,6 @@
<template>
<view :style="themeColor">
<view class="content" :style="searchPageStyle">
<view class="content" :style="tabBarPagePaddingStyle">
<view class="cate-search">
<view class="search-box" @click="search()" @tap.stop="search()">
<input class="uni-input" maxlength="50" v-model="inputValue" confirm-type="search" @focus="inputFocus" focus @confirm="search()" @click="search()" @tap="search()" :placeholder="searchWords ? searchWords : ''" />
@@ -62,13 +62,6 @@
isAllHistory: false
};
},
computed: {
searchPageStyle() {
return this.$util.objToStyle({
paddingBottom: this.tabBarList && this.tabBarList.list ? '112rpx' : '0'
});
}
},
onLoad(options) {
if (options.keyword) this.inputValue = options.keyword;
uni.getStorageSync('search') ? '' : uni.setStorageSync('search', []);

View File

@@ -1,6 +1,6 @@
<template>
<view :style="themeColor">
<mescroll-uni @getData="getData" ref="mescroll" :style="noticeListPageStyle">
<mescroll-uni @getData="getData" ref="mescroll" :style="tabBarPagePaddingStyle">
<block slot="list">
<view class="notice-list" v-if="dataList.length">
<view class="notice-item" @click="jumpDetail(item.id)" v-for="(item, index) in dataList"
@@ -37,13 +37,6 @@ export default {
dataList: []
};
},
computed: {
noticeListPageStyle() {
return this.$util.objToStyle({
paddingBottom: this.tabBarList && this.tabBarList.list ? '112rpx' : '0'
});
}
},
onShow() {
this.setPublicShare();
},