移动端应用外壳

用 AppLayout 搭移动端母版,[AppMenu] 声明式注入底部 Tab,登录后按设备自动落到 /app。

最后更新

同一套 Known 应用,桌面端跑 AdminLayout(侧边栏 + 顶栏),移动端则更适合底部 Tab 的 App 外壳。 框架把这件事拆成三块可组合的原语:AppLayout 母版、[AppMenu] 声明式底部导航、以及登录页的设备判定自动落位。 理念与 AdminLayout 一致——声明一次、自动推导,页面不必逐页接线。

AppLayout 母版

AppLayout 是移动端骨架:顶部 App 栏(标题 + 刷新 + 主题)+ 可滚动内容区 + 底部 Tab 导航。 页面用 @layout AppLayout 挂上它即可(AdminLayout 是路由默认布局,移动端页面需显式声明自己的布局):

@page "/app"
@layout AppLayout

<div class="app-hero">
    <span class="app-hero-title">管理员,欢迎回来</span>
</div>
@* 你的移动端内容 *@

它内建:

  • 登录门禁:进入时先取本地会话,未命中再向后端恢复;仍无身份则跳登录页(与 AdminLayout 同一套 EnsureAuth)。
  • 标题自动推导:顶栏标题取当前 URL 命中的 Tab 名称,命中不到再回退 SiteTitle;要固定标题就传 Title。
  • 软刷新:右上角刷新按钮递增重挂键,令 @Body 容器重建、页面重新初始化取数,不重载浏览器。
  • 明暗主题 / 安全区:主题跟随 LocalService,底部栏用 env(safe-area-inset-bottom) 适配刘海屏。

参数一览见 AppLayout 组件文档。

用 [AppMenu] 注入底部 Tab

底部 Tab 不写在布局里,而是用类级特性 [AppMenu] 集中声明在 App 首页(外壳页)上。 [AppMenu] 可重复标注,一条 = 一个 Tab;框架扫描模块程序集收集它们,交给 AppMenuProvider 汇总为扁平列表:

@page "/app"
@layout AppLayout
@attribute [AppMenu("首页", Icon = "home",           Url = "/app",         Order = 1)]
@attribute [AppMenu("消息", Icon = "message-circle", Url = "/app/messages", Order = 2)]
@attribute [AppMenu("我的", Icon = "user",           Url = "/app/mine",     Order = 3)]

AppLayout 会自动把这三条渲染成底部三个 Tab,并按当前路由高亮命中的那一个。 各字段语义:

  • name:Tab 文字,可作为国际化 key(经 LocalService 解析,无译文回退原文)。
  • icon:对应 Icon 组件的 Type;留空回退 circle。
  • url:目标路由;留空时回退到标注所在页面自身的 @page 路由。
  • order:排序,越小越靠前。
  • roles:逗号分隔的可见角色,语义同 [Menu].Roles——留空则所有已登录用户可见。
  • hidden:为 true 时不进 Tab(路由仍可直达)。

Tab 的目标页同样 @layout AppLayout 即可共享这套外壳,切换时底部栏常驻、标题与高亮自动跟随:

@page "/app/mine"
@layout AppLayout
@* “我的”页无需再标 [AppMenu]——整组 Tab 已在 /app 上声明 *@

与 [Menu](侧边栏树)不同,[AppMenu] 是扁平的,没有分组层级——底部 Tab 本就不该有二级菜单。

登录后按设备自动落位

移动端最自然的路径是:手机浏览器登录成功后直接进 /app,而不是先落到桌面后台再手动跳。 框架在 LocalService 里做了设备判定(known.device.isMobile():UA + 触屏 + 窄屏 + iPadOS 兜底), 登录页据此决定落位地址:

// 登录成功后(KnownApp/Pages/Login.razor 的默认实现)
// 优先级:显式 ?redirect > 移动端 Config.AppHomeRoute > 桌面 "/"
return await Local.IsMobileAsync() ? Config.AppHomeRoute : "/";

落位地址由 Config.AppHomeRoute 控制,默认 "/app";若你的移动端首页不叫 /app,启动时覆盖一次即可:

Config.AppHomeRoute = "/m/home";

判定结果缓存在 LocalService.Info.IsMobile,AppLayout、页面或任何注入 LocalService 的地方都能读到, 用于按端切换展示密度。

常见坑

  • 移动端页面没加 @layout AppLayout → 它会被路由默认布局 AdminLayout 接管,于是手机上看到的是桌面后台。每个 App 页都要显式声明。
  • [AppMenu] 标了但底部没 Tab → 标注所在程序集须被 Config.AddModule 扫描到;放在未登记的程序集里,特性收集不到。
  • 以为要逐页传 Tab → 不用。整组 Tab 在外壳页声明一次,AppMenuProvider 全站装配;换页只换 @Body,底部栏常驻。
  • roles 过滤后 Tab 变少 → 正常,AppMenuProvider.Build(user) 按当前登录用户角色裁剪;未登录时可见项为空。
  • 桌面端也跳到了 /app → 检查设备判定:桌面窄窗口(pointer: coarse 且宽度 ≤ 768)会被判为移动端;真桌面一般不触发。
  • 想改移动端首页路由 → 改 Config.AppHomeRoute,并让那台页面上的 [AppMenu] 的 Url 与之对齐。

相关

微信公众号二维码 扫码关注公众号
QQ 交流群二维码 扫码加入 QQ 群