feat: tab的调整

This commit is contained in:
zoujing committed 2026-09-18 16:53:52 +08:00
1 parent 9475a3cc3a
commit f93b9f034f
8 files changed
+439 -300

No files matched your search

+89 -23
View File
@@ -1,9 +1,65 @@
/**
* 导航工具:登录回跳、Tab 切换、返回兜底
* 本项目未使用原生 tabBar(Tab 栏为自绘组件),因此 Tab 之间用 redirectTo 切换。
* ---------------------------------------------------------------------------
* 三个 Tab 不是三个页面,而是主包壳页 pages/home/index.vue 里的三块面板(views/*):
* 同屏常驻、切 Tab 只切面板可见性 —— Tab 栏因此只渲染一次,切 Tab 不会闪烁、不重建、不重复请求。
* 壳在 onMounted 时用 registerTabController({ go, getActive }) 把切换器交给本模块,
* 页面内部(如「我的页 → 我的权益」)调的 switchTabTo 即转为调它,不再走路由。
*
* ⚠️ 面板不是页面,没有 query 可用 → Tab 间参数用内存透传(setTabQuery / consumeTabQuery)。
* ⚠️ 壳未挂载时切 Tab(子页 / 登录页 / 冷启)→ 记 pendingTab 并 reLaunch 回壳,由壳 onMounted 消费。
*/
import { ROUTES, STORAGE_KEYS } from "@/constant";
/** 壳内面板 key */
const TAB_KEYS = ["home", "benefit", "mine"];
/** 壳注册进来的 Tab 切换器 */
let tabCtl = { go: null, getActive: () => "home" };
export const registerTabController = (ctl) => {
tabCtl = {
go: (ctl && ctl.go) || null,
getActive: (ctl && ctl.getActive) || (() => "home"),
};
};
/** Tab 间透传的参数 */
let tabQuery = "";
/** 壳尚未就绪时的待切 Tab */
let pendingTab = null;
export const setTabQuery = (query) => {
tabQuery = query || "";
};
/** 取出并清空透传参数(仅在目标面板 onPanelShow 调用一次) */
export const consumeTabQuery = () => {
const query = tabQuery;
tabQuery = "";
return query;
};
/** 取出并清空待切 Tab(仅在壳 onMounted 调用一次) */
export const consumePendingTab = () => {
const pending = pendingTab;
pendingTab = null;
return pending;
};
/** query 串取参数 */
const pickParam = (query, key) => {
const matched = (query || "").match(new RegExp(`(?:^|&)${key}=([^&]*)`));
return matched ? matched[1] : "";
};
/** query 串去掉某个参数 */
const dropParam = (query, key) =>
(query || "")
.split("&")
.filter((kv) => kv && !new RegExp(`^${key}=`).test(kv))
.join("&");
/** 当前页面路径(含参数) */
export const currentRoute = () => {
const pages = getCurrentPages();
@@ -18,6 +74,13 @@ export const currentRoute = () => {
return `/${page.route}${query ? `?${query}` : ""}`;
};
/** 壳页地址补上当前 Tab(否则登录后只能落回主页) */
const withTab = (url) => {
const [path, query = ""] = (url || "").split("?");
if (path !== ROUTES.HOME || /(?:^|&)tab=/.test(query)) return url;
return `${path}?tab=${tabCtl.getActive()}${query ? `&${query}` : ""}`;
};
/** 是否已登录,未登录则跳登录页并带上 redirect */
export const requireLogin = (silent = false) => {
const token = uni.getStorageSync(STORAGE_KEYS.ACCESS_TOKEN) || "";
@@ -31,28 +94,28 @@ export const requireLogin = (silent = false) => {
/** 跳登录页,redirect 为登录后的回跳地址 */
export const goLogin = (redirect) => {
const target = redirect || currentRoute();
const target = withTab(redirect || currentRoute());
const url = `${ROUTES.LOGIN}${target ? `?redirect=${encodeURIComponent(target)}` : ""}`;
uni.navigateTo({ url });
};
/** 登录成功后的回跳 */
/** 登录成功后的回跳:目标是壳页 → 切回原 Tab(含原分段);其余页面走 redirectTo */
export const goAfterLogin = (redirect) => {
if (redirect) {
const url = decodeURIComponent(redirect);
const pages = getCurrentPages();
const top = pages.length ? `/${pages[pages.length - 1].route}` : "";
if (top && url.startsWith(top)) {
uni.navigateBack();
return;
}
uni.redirectTo({
url,
if (!redirect) {
uni.navigateBack({
fail: () => uni.reLaunch({ url: ROUTES.HOME }),
});
return;
}
uni.navigateBack({
const url = decodeURIComponent(redirect);
const [path, query = ""] = url.split("?");
if (path === ROUTES.HOME) {
const rest = dropParam(query, "tab");
switchTabTo(pickParam(query, "tab") || tabCtl.getActive(), rest ? `?${rest}` : "");
return;
}
uni.redirectTo({
url,
fail: () => uni.reLaunch({ url: ROUTES.HOME }),
});
};
@@ -67,18 +130,21 @@ export const goBack = () => {
}
};
/** Tab 切换 */
/**
* 切 Tab:已在壳页 → 交由壳切面板(不重建、不重复请求);
* 不在壳页(子页 / 登录页 / 冷启)→ 记待办并 reLaunch 回壳,由壳 onMounted 消费。
* extraQuery 形如 "?seg=mine",由目标面板 onPanelShow 取用。
*/
export const switchTabTo = (key, extraQuery = "") => {
const map = {
home: ROUTES.HOME,
benefit: ROUTES.BENEFIT,
mine: ROUTES.MINE,
};
const url = `${map[key] || ROUTES.HOME}${extraQuery}`;
if (!TAB_KEYS.includes(key)) return;
const pages = getCurrentPages();
const top = pages.length ? `/${pages[pages.length - 1].route}` : "";
if (top === url.split("?")[0]) return;
uni.redirectTo({ url });
if (top === ROUTES.HOME && tabCtl.go) {
tabCtl.go(key, extraQuery);
return;
}
pendingTab = { key, query: extraQuery };
uni.reLaunch({ url: ROUTES.HOME });
};
/** 跳板块列表页 */