优化 router,支持 isAuth,并添加示例

This commit is contained in:
icssoa
2025-09-23 15:59:19 +08:00
parent 30750cc164
commit 3f988f2c31
10 changed files with 202 additions and 71 deletions

View File

@@ -64,7 +64,7 @@ if (isArray(ctx.subPackages)) {
PAGES.push({ PAGES.push({
path: a.root + "/" + b.path, // 拼接子包根路径和页面路径 path: a.root + "/" + b.path, // 拼接子包根路径和页面路径
style: b.style, style: b.style,
meta: b.meta ?? {}, meta: b.meta
}); });
}); });
}); });

View File

@@ -1,11 +1,24 @@
import { PAGES, TABS } from "../ctx"; import { PAGES, TABS } from "../ctx";
import type { BackOptions, PageInstance, PushOptions } from "../types"; import type { BackOptions, PageInstance, PushOptions } from "../types";
import { storage, last, isNull, isEmpty, get, isFunction, toArray, map, debounce } from "../utils"; import {
storage,
last,
isNull,
isEmpty,
get,
isFunction,
toArray,
map,
debounce,
nth
} from "../utils";
// 路由信息类型 // 路由信息类型
type RouteInfo = { type RouteInfo = {
path: string; path: string;
meta?: UTSJSONObject; query: UTSJSONObject;
meta: UTSJSONObject;
isAuth?: boolean;
}; };
// 跳转前钩子类型 // 跳转前钩子类型
@@ -21,15 +34,11 @@ type Events = {
// 路由核心类 // 路由核心类
export class Router { export class Router {
private _events = {} as Events; // 事件存储 private eventsMap = {} as Events; // 事件存储
// 获取缓存的路由参数 // 获取缓存的路由参数
params() { params() {
const data = storage.get("router-params") as UTSJSONObject; return (storage.get("router-params") ?? {}) as UTSJSONObject;
if (isNull(data)) {
return {} as UTSJSONObject;
}
return data;
} }
// 获取默认路径,支持 home 和 login // 获取默认路径,支持 home 和 login
@@ -38,6 +47,7 @@ export class Router {
home: PAGES[0].path, // 首页为第一个页面 home: PAGES[0].path, // 首页为第一个页面
login: "/pages/user/login" login: "/pages/user/login"
}; };
return get(paths, name) as string; return get(paths, name) as string;
} }
@@ -45,31 +55,38 @@ export class Router {
getPages(): PageInstance[] { getPages(): PageInstance[] {
return map(getCurrentPages(), (e) => { return map(getCurrentPages(), (e) => {
let path = e.route!; let path = e.route!;
// 根路径自动转为首页 // 根路径自动转为首页
if (path == "/") { if (path == "/") {
path = this.defaultPath("home"); path = this.defaultPath("home");
} }
// 补全路径前缀 // 补全路径前缀
if (!path.startsWith("/")) { if (!path.startsWith("/")) {
path = "/" + path; path = "/" + path;
} }
// 获取页面样式 // 获取页面样式
const page = PAGES.find((e) => e.path == path); const page = PAGES.find((e) => e.path == path);
const style = page?.style; const style = page?.style;
const meta = page?.meta; const meta = page?.meta;
// 获取页面暴露的方法 // 获取页面暴露的方法
// @ts-ignore // @ts-ignore
let exposed = e.vm as any; const vm = e.vm as any;
let exposed = vm;
// #ifdef H5 // #ifdef H5
exposed = get(e, "vm.$.exposed"); exposed = get(e, "vm.$.exposed");
// #endif // #endif
// 获取页面 query 参数 // 获取页面 query 参数
const query = (get(e, "options") ?? {}) as UTSJSONObject; const query = (get(e, "options") ?? {}) as UTSJSONObject;
return { return {
path, path,
// @ts-ignore vm,
vm: e.vm!,
// @ts-ignore
exposed, exposed,
style, style,
meta, meta,
@@ -113,7 +130,8 @@ export class Router {
complete, complete,
animationType, animationType,
animationDuration, animationDuration,
events events,
isAuth
} = options; } = options;
// 拼接 query 参数到 url // 拼接 query 参数到 url
@@ -176,15 +194,15 @@ export class Router {
}; };
// 跳转前钩子处理 // 跳转前钩子处理
if (isFunction(this._events["beforeEach"])) { if (this.eventsMap.beforeEach != null) {
const meta = PAGES.find((e) => e.path == path)?.meta; // 当前页
const from = this.getPages().slice(-1)[0]; const from = last(this.getPages());
const to: RouteInfo = { path, meta: meta ?? {} };
(this._events["beforeEach"] as BeforeEach)( // 跳转页
to, const to = { path, meta: this.getMeta(path), query, isAuth } as RouteInfo;
from,
next // 调用跳转前钩子
); this.eventsMap.beforeEach(to, from!, next);
} else { } else {
next(); next();
} }
@@ -197,31 +215,49 @@ export class Router {
}); });
} }
// 返回上一页,若为首页则回首页 // 返回上一页
back(options: BackOptions | null = null) { back(options: BackOptions | null = null) {
let path = '' if (this.isFirstPage()) {
const next = ()=>{ this.home();
if (this.isFirstPage()) { } else {
this.home(); const delta = options?.delta ?? 1;
path = this.defaultPath("home")
} else { // 执行跳转函数
path = this.getPages().slice(-2)[0].path; const next = () => {
uni.navigateBack({ ...(options ?? {}) }); uni.navigateBack({ ...(options ?? {}) });
};
// 跳转前钩子处理
if (this.eventsMap.beforeEach != null) {
// 当前页
const from = last(this.getPages());
// 上一页
const to = nth(this.getPages(), -delta - 1);
if (to != null) {
// 调用跳转前钩子
this.eventsMap.beforeEach(
{
path: to.path,
query: to.query,
meta: to.meta ?? ({} as UTSJSONObject)
},
from!,
next
);
} else {
console.error("[router] found to page is null");
}
} else {
next();
} }
} }
// 跳转前钩子处理 }
if (isFunction(this._events["beforeEach"])) {
const meta = PAGES.find((e) => e.path == path)?.meta; // 获取页面元数据
const from = this.getPages().slice(-1)[0]; getMeta(path: string) {
const to: RouteInfo = { path, meta: meta ?? {} }; return PAGES.find((e) => e.path.includes(path))?.meta ?? ({} as UTSJSONObject);
(this._events["beforeEach"] as BeforeEach)(
to,
from,
next
);
} else {
next();
}
} }
// 执行当前页面暴露的方法 // 执行当前页面暴露的方法
@@ -296,21 +332,21 @@ export class Router {
}); });
} }
// 登录后回调 // 登录后回调
if (isFunction(this._events["afterLogin"])) { if (this.eventsMap.afterLogin != null) {
(this._events["afterLogin"] as AfterLogin)(); this.eventsMap.afterLogin!();
} }
// 触发全局 afterLogin 事件 // 触发全局 afterLogin 事件
uni.$emit("afterLogin"); uni.$emit("afterLogin");
} }
// 注册跳转前钩子 // 注册跳转前钩子
beforeEach(callback: BeforeEach) { beforeEach(cb: BeforeEach) {
this._events["beforeEach"] = callback; this.eventsMap.beforeEach = cb;
} }
// 注册登录后回调 // 注册登录后回调
afterLogin(callback: AfterLogin) { afterLogin(cb: AfterLogin) {
this._events["afterLogin"] = callback; this.eventsMap.afterLogin = cb;
} }
} }

View File

@@ -37,8 +37,9 @@ export type PushOptions = {
path: string; path: string;
mode?: PushMode; mode?: PushMode;
events?: any; events?: any;
query?: any; query?: UTSJSONObject;
params?: any; isAuth?: boolean;
params?: UTSJSONObject;
animationType?: PushAnimationType; animationType?: PushAnimationType;
animationDuration?: number; animationDuration?: number;
success?: (result: any) => void; success?: (result: any) => void;

View File

@@ -1,13 +1,11 @@
{ {
"name": "cool-unix", "name": "cool-unix",
"appid": "__UNI__EC807C1", "appid": "__UNI__651711F",
"description": "完全开源、永久免费、上手容易、效率极高的开发脚手架", "description": "完全开源、永久免费、上手容易、效率极高的开发脚手架",
"versionName": "1.0.0", "versionName": "1.0.0",
"versionCode": "100", "versionCode": "100",
"uni-app-x": {}, "uni-app-x": {},
/* */
"quickapp": {}, "quickapp": {},
/* */
"mp-weixin": { "mp-weixin": {
"darkmode": true, "darkmode": true,
"appid": "wxdebc4de0b5584ca4", "appid": "wxdebc4de0b5584ca4",
@@ -93,4 +91,4 @@
"splashScreens": {} "splashScreens": {}
} }
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "cool-unix", "name": "cool-unix",
"version": "8.0.25", "version": "8.0.26",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"build-ui": "node ./uni_modules/cool-ui/scripts/generate-types.js", "build-ui": "node ./uni_modules/cool-ui/scripts/generate-types.js",

View File

@@ -28,7 +28,7 @@
"style": { "style": {
"navigationBarTitleText": "设置" "navigationBarTitleText": "设置"
}, },
"meta":{ "meta": {
"isAuth": true "isAuth": true
} }
}, },
@@ -37,7 +37,7 @@
"style": { "style": {
"navigationBarTitleText": "通用设置" "navigationBarTitleText": "通用设置"
}, },
"meta":{ "meta": {
"isAuth": true "isAuth": true
} }
}, },
@@ -46,7 +46,7 @@
"style": { "style": {
"navigationBarTitleText": "通知设置" "navigationBarTitleText": "通知设置"
}, },
"meta":{ "meta": {
"isAuth": true "isAuth": true
} }
}, },
@@ -55,7 +55,7 @@
"style": { "style": {
"navigationBarTitleText": "" "navigationBarTitleText": ""
}, },
"meta":{ "meta": {
"isAuth": true "isAuth": true
} }
}, },
@@ -64,7 +64,7 @@
"style": { "style": {
"navigationBarTitleText": "联系客服" "navigationBarTitleText": "联系客服"
}, },
"meta":{ "meta": {
"isAuth": true "isAuth": true
} }
} }
@@ -78,7 +78,7 @@
"style": { "style": {
"navigationBarTitleText": "编辑资料" "navigationBarTitleText": "编辑资料"
}, },
"meta":{ "meta": {
"isAuth": true "isAuth": true
} }
}, },
@@ -87,7 +87,7 @@
"style": { "style": {
"navigationStyle": "custom" "navigationStyle": "custom"
}, },
"meta":{ "meta": {
"isAuth": true "isAuth": true
} }
}, },
@@ -96,7 +96,7 @@
"style": { "style": {
"navigationStyle": "custom" "navigationStyle": "custom"
}, },
"meta":{ "meta": {
"isAuth": true "isAuth": true
} }
}, },
@@ -489,6 +489,15 @@
"style": { "style": {
"navigationBarTitleText": "Animation 动画" "navigationBarTitleText": "Animation 动画"
} }
},
{
"path": "other/router/index",
"style": {
"navigationBarTitleText": "Router 路由"
}
},
{
"path": "other/router/query"
} }
] ]
}, },
@@ -512,6 +521,9 @@
"style": { "style": {
"navigationBarTitleText": "收货地址", "navigationBarTitleText": "收货地址",
"enablePullDownRefresh": true "enablePullDownRefresh": true
},
"meta": {
"isAuth": true
} }
}, },
{ {

View File

@@ -0,0 +1,41 @@
<template>
<cl-page>
<view class="p-3">
<demo-item :label="t('跳转')">
<cl-button @click="toPush">{{ t("跳转") }}</cl-button>
</demo-item>
<demo-item :label="t('带参数')">
<cl-button @click="toQuery">{{ t("跳转") }}</cl-button>
</demo-item>
<demo-item :label="t('需登录')">
<cl-button @click="toLogin">{{ t("跳转") }}</cl-button>
</demo-item>
</view>
</cl-page>
</template>
<script lang="ts" setup>
import { router, uuid } from "@/cool";
import DemoItem from "../../components/item.uvue";
import { t } from "@/locale";
function toPush() {
router.to("/pages/demo/other/router/query");
}
function toQuery() {
router.push({ path: "/pages/demo/other/router/query", query: { id: uuid() } });
}
function toLogin() {
router.push({
path: "/pages/demo/other/router/query",
query: { id: uuid() },
isAuth: true
});
}
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,29 @@
<template>
<cl-page>
<view class="p-3">
<demo-item :label="t('ID')">
<cl-text>{{ id ?? "-" }}</cl-text>
</demo-item>
<demo-item :label="t('用户信息')" v-if="!user.isNull()">
<cl-text>{{ userInfo?.nickName ?? "-" }}</cl-text>
</demo-item>
</view>
</cl-page>
</template>
<script lang="ts" setup>
import { t } from "@/locale";
import DemoItem from "../../components/item.uvue";
import { userInfo, useStore } from "@/cool";
const { user } = useStore();
const props = defineProps({
id: {
type: String
}
});
</script>
<style lang="scss" scoped></style>

View File

@@ -46,9 +46,9 @@
<view class="p-3"> <view class="p-3">
<view class="group" v-for="item in data" :key="item.label"> <view class="group" v-for="item in data" :key="item.label">
<cl-text :pt="{ className: '!text-sm !text-surface-400 mb-2 ml-2' }">{{ <cl-text :pt="{ className: '!text-sm !text-surface-400 mb-2 ml-2' }"
item.label >{{ item.label }}{{ item.children?.length ?? 0 }}</cl-text
}}{{ item.children?.length ?? 0 }}</cl-text> >
<view class="list"> <view class="list">
<cl-row :gutter="10"> <cl-row :gutter="10">
@@ -461,6 +461,11 @@ const data = computed<Item[]>(() => {
label: "Animation", label: "Animation",
icon: "instance-line", icon: "instance-line",
path: "/pages/demo/other/animation" path: "/pages/demo/other/animation"
},
{
label: "Router",
icon: "compass-discover-line",
path: "/pages/demo/other/router/index"
} }
] ]
} }

View File

@@ -1,8 +1,17 @@
import { router, useStore } from "@/cool"; import { router, useStore } from "@/cool";
router.beforeEach((to, _, next) => { /**
* 路由跳转前的全局钩子(如修改 pages.json 后需重新编译项目以确保路由信息生效)
* @param to 跳转页
* @param from 当前页
* @param next 跳转函数
*/
router.beforeEach((to, from, next) => {
const { user } = useStore(); const { user } = useStore();
if (to.meta?.isAuth == true) {
// 判断是否需要登录
if (to.isAuth == true || to.meta?.isAuth == true) {
// 如果用户信息为空,则跳转到登录页
if (!user.isNull()) { if (!user.isNull()) {
next(); next();
} else { } else {
@@ -11,4 +20,4 @@ router.beforeEach((to, _, next) => {
} else { } else {
next(); next();
} }
}); });