hookehuyr

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

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