演示

添加浮动操作按钮

使用 @quan-erp/shared-ui 的 FloatingActionButton、FloatingButton、FloatingContainer 添加仅移动端的主操作。用 isMobile 控制显示,并在底栏可见时调整位置。

目标

为移动列表页提供清晰的主操作(创建、快捷添加、多操作展开),避免挤占页头——桌面端动作仍留在 PageTitle / 工具栏。

  • FAB 仅移动端——桌面不显示
  • 从 @quan-erp/shared-ui 导入 FloatingActionButton、FloatingButton、FloatingContainer
  • 用 useIsContainInBottomNavBar 切换 bottom-25 与 bottom-5
  • 与响应式卡片列表配合,不要只对着密集表格放 FAB

1. 前置条件

需要已按 isMobile 分支(或即将分支)的页面,并知道用于底栏检查的路由 path。

  • 插件前端已安装 @quan-erp/shared-ui 与 @quan-erp/base-frontend
  • 页面路径在 /${metadata.name}/… 下,经 AppRegistry.route.add() 注册
  • 已有移动端卡片布局,FAB 才有可操作的列表

2. 用 isMobile + 底栏做门禁

渲染 FAB 前先计算 isMobile,以及该 path 是否出现在底栏。

  • isMobile — 桌面完全隐藏 FAB
  • isContainInBottomNav — path 在底栏列表中时为 true
  • className — absolute + bottom-25(底栏可见)或 bottom-5(隐藏)
  • adaptivePosition={true} — 建议开启以避开安全区
TSXhooks
1import { 2 FloatingActionButton, 3 FloatingButton, 4 FloatingContainer, 5 Button, 6 SCREENS, 7 useMediaQuery, 8 cn, 9} from "@quan-erp/shared-ui"; 10import { useIsContainInBottomNavBar } from "@quan-erp/base-frontend"; 11import { Plus } from "@icon-park/react"; 12import metadata from "../../module.metadata.json" with { type: "json" }; 13 14const isMobile = useMediaQuery(SCREENS.md); 15const isContainInBottomNav = useIsContainInBottomNavBar( 16 `/${metadata.name}/my-page`, 17);

3. 单按钮

一个主操作(通常是创建)。设置 expandable={false},在 FloatingButton 内包裹 Button。

  • 仅在 {isMobile && (…)} 内渲染
  • onClick 打开与桌面 PageTitle 相同的创建对话框 / sheet
  • 图标 Button 使用 size="icon-lg" 以符合 shared-ui
JSONsingle FAB
1{isMobile && ( 2 <FloatingActionButton 3 className={cn( 4 "absolute", 5 isContainInBottomNav ? "bottom-25" : "bottom-5", 6 )} 7 adaptivePosition={true} 8 expandable={false} 9 > 10 <FloatingButton> 11 <Button size="icon-lg" onClick={() => handleCreate()}> 12 <Plus /> 13 </Button> 14 </FloatingButton> 15 </FloatingActionButton> 16)}

4. 可展开多按钮

多个相关操作共用一个 FAB 时,设置 rowSpan 与 expandClassName;触发器放 FloatingButton,操作放 FloatingContainer。

  • rowSpan — 展开面板中的操作行数
  • expandClassName — 展开容器宽度
  • 文案保持简短;每个 Button 应调用与桌面菜单相同的处理函数
JSONexpandable
1{isMobile && ( 2 <FloatingActionButton 3 rowSpan={2} 4 expandClassName="w-[15rem]" 5 adaptivePosition={true} 6 className={cn( 7 "absolute", 8 isContainInBottomNav ? "bottom-25" : "bottom-5", 9 )} 10 > 11 <FloatingButton> 12 <Plus /> 13 </FloatingButton> 14 <FloatingContainer> 15 <Button className="w-full h-full" onClick={() => actionOne()}> 16 Action One 17 </Button> 18 <Button className="w-full h-full" onClick={() => actionTwo()}> 19 Action Two 20 </Button> 21 </FloatingContainer> 22 </FloatingActionButton> 23)}

5. 验证

分别检查移动端与桌面端,以及是否属于底栏。

  • 桌面 — 不见 FAB;PageTitle / 工具栏仍有创建
  • 移动、path 不在底栏 — FAB 在 bottom-5,点击打开创建
  • 移动、path 在底栏 — FAB 在 bottom-25,避开底栏
  • 可展开 — 点击展开;两个操作均正确触发
  • 跨 SCREENS.md 旋转/缩放 — FAB 随 isMobile 出现/消失

常见错误

  • 桌面显示 FAB — 与 PageTitle 重复且观感错误
  • 始终写死 bottom-5 — FAB 被底栏挡住
  • useIsContainInBottomNavBar 的 path 错误 — 位置永不偏移
  • 没有移动卡片列表就放 FAB — 浮在不可用的密集表格上
  • FAB 与桌面使用不同的创建处理函数 — 行为漂移