目标
为移动列表页提供清晰的主操作(创建、快捷添加、多操作展开),避免挤占页头——桌面端动作仍留在 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 与桌面使用不同的创建处理函数 — 行为漂移