hookehuyr

fix(JlsBottomNav): 调整底栏加载失败或无数据时仅显示首页

新增getHomeOnlyTabbarItems工具方法用于获取仅包含首页的底栏项,替换所有兜底场景的默认项获取逻辑,更新README补充兜底规则说明,并补充缺失的组件类型声明
......@@ -13,6 +13,10 @@ declare module '@vue/runtime-core' {
AudioBackground1: typeof import('./src/components/audioBackground1.vue')['default']
AudioList: typeof import('./src/components/audioList.vue')['default']
BottomNav: typeof import('./src/components/BottomNav.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton']
ElInput: typeof import('element-plus/es')['ElInput']
ElOption: typeof import('element-plus/es')['ElOption']
ElSelect: typeof import('element-plus/es')['ElSelect']
Floor: typeof import('./src/components/Floor/index.vue')['default']
InfoPopup: typeof import('./src/components/InfoPopup.vue')['default']
InfoPopupLite: typeof import('./src/components/InfoPopupLite.vue')['default']
......@@ -27,14 +31,19 @@ declare module '@vue/runtime-core' {
SvgIcon: typeof import('./src/components/Floor/svgIcon.vue')['default']
VanBackTop: typeof import('vant/es')['BackTop']
VanButton: typeof import('vant/es')['Button']
VanCol: typeof import('vant/es')['Col']
VanConfigProvider: typeof import('vant/es')['ConfigProvider']
VanDialog: typeof import('vant/es')['Dialog']
VanField: typeof import('vant/es')['Field']
VanFloatingPanel: typeof import('vant/es')['FloatingPanel']
VanIcon: typeof import('vant/es')['Icon']
VanImage: typeof import('vant/es')['Image']
VanImagePreview: typeof import('vant/es')['ImagePreview']
VanLoading: typeof import('vant/es')['Loading']
VanOverlay: typeof import('vant/es')['Overlay']
VanPopup: typeof import('vant/es')['Popup']
VanRow: typeof import('vant/es')['Row']
VanSlider: typeof import('vant/es')['Slider']
VanSwipe: typeof import('vant/es')['Swipe']
VanSwipeItem: typeof import('vant/es')['SwipeItem']
VanTab: typeof import('vant/es')['Tab']
......
......@@ -9,7 +9,7 @@
- [tabbar.api.js](/Users/huyirui/program/itomix/git/map-demo/src/components/JlsBottomNav/tabbar.api.js)
底栏配置接口请求,当前使用 `/srv/?a=app_menu`
- [tabbar.utils.js](/Users/huyirui/program/itomix/git/map-demo/src/components/JlsBottomNav/tabbar.utils.js)
底栏数据归一化、默认项、顺序、icon class 解析、目标地址解析。
底栏数据归一化、首页兜底、顺序、icon class 解析、目标地址解析。
- [useTabbar.js](/Users/huyirui/program/itomix/git/map-demo/src/components/JlsBottomNav/useTabbar.js)
组件内状态和加载逻辑,支持 `api` / `mock` 两种加载模式。
- [nav-state.js](/Users/huyirui/program/itomix/git/map-demo/src/components/JlsBottomNav/nav-state.js)
......@@ -45,6 +45,15 @@ import JlsBottomNav from '@/components/JlsBottomNav';
`visible``true` 时组件才会渲染并尝试加载数据。
接口显示规则:
- 只有接口成功并返回有效菜单时,才显示接口里的其他按钮
- 如果接口返回 `data: []`
- 如果接口失败
- 如果接口结构异常
以上情况都统一只保留 `home`
### 3. 激活态
组件通过当前路由的 `activeTab` 识别高亮项,兼容以下写法:
......@@ -126,6 +135,9 @@ import { getJlsTabbarPreviewOptions } from '@/components/JlsBottomNav/preview';
## 注意点
- 组件默认按小程序 WebView 跳转方式调用 `wx.miniProgram.redirectTo / reLaunch`
- 底栏显示兜底规则是“只保留首页”:
- 只有接口成功并返回有效菜单时,才显示首页之外的其他项
- 接口空数组、失败、异常、无法归一化时,都只显示 `home`
- 当前跳转逻辑是:
- `home` 默认跳回小程序页 `/pages/index/index`
- 其他项不会直接跳裸 H5 URL,而是优先使用接口返回的 `page_url`
......
......@@ -111,6 +111,8 @@ export const getDefaultTabbarItem = (key) => {
export const getDefaultTabbarItems = () =>
TABBAR_ORDER.map((key) => getDefaultTabbarItem(key)).filter(Boolean);
export const getHomeOnlyTabbarItems = () => [getDefaultTabbarItem('home')].filter(Boolean);
const buildTabbarPageUrl = (key, webviewUrl, webviewTitle, rawPageUrl = '') => {
if (key === 'home') {
return rawPageUrl || '/pages/index/index';
......@@ -224,12 +226,12 @@ const normalizeTabbarObjectItems = (rawMap = {}) => {
export const normalizeTabbarItems = (rawItems = []) => {
if (!Array.isArray(rawItems) || !rawItems.length) {
return getDefaultTabbarItems();
return getHomeOnlyTabbarItems();
}
const normalizedItems = rawItems.map((item) => normalizeTabbarItem(item)).filter(Boolean);
return normalizedItems.length ? normalizedItems : getDefaultTabbarItems();
return normalizedItems.length ? normalizedItems : getHomeOnlyTabbarItems();
};
export const normalizeTabbarPayload = (rawPayload = null) => {
......@@ -245,7 +247,7 @@ export const normalizeTabbarPayload = (rawPayload = null) => {
const objectPayload = rawPayload?.tab_items || rawPayload?.tabs || rawPayload?.menus || rawPayload;
const normalizedItems = normalizeTabbarObjectItems(objectPayload);
return normalizedItems.length ? normalizedItems : getDefaultTabbarItems();
return normalizedItems.length ? normalizedItems : getHomeOnlyTabbarItems();
};
export const resolveTabbarTargetUrl = (item = {}) => {
......
......@@ -2,14 +2,14 @@ import { computed, reactive, readonly } from 'vue';
import { getTabbarConfigAPI } from './tabbar.api';
import {
getDefaultTabbarItem,
getDefaultTabbarItems,
getHomeOnlyTabbarItems,
normalizeTabbarKey,
normalizeTabbarPayload,
resolveTabbarTargetUrl,
} from './tabbar.utils';
const state = reactive({
tabItems: getDefaultTabbarItems(),
tabItems: getHomeOnlyTabbarItems(),
loaded: false,
loading: false,
loadMode: 'api',
......@@ -18,7 +18,7 @@ const state = reactive({
let tabbarRequestPromise = null;
const applyFallbackItems = () => {
state.tabItems = getDefaultTabbarItems();
state.tabItems = getHomeOnlyTabbarItems();
};
const resolveLoadMode = (options = {}) => (options.useMock ? 'mock' : 'api');
......