n-fab 悬浮动作按钮
用于新建、客服、分享等常驻操作入口。组件内部处理四角定位、四向菜单、拖拽、边缘吸附、遮罩与禁用,调用者只需配置数据、属性和插槽。
平台兼容性
| App | H5 | 微信小程序 | 支付宝小程序 | 百度小程序 | 头条小程序 | QQ小程序 |
|---|---|---|---|---|---|---|
| √ | √ | √ | × | × | × | × |
如何调用
<n-fab v-model:active="active" :list="actions" @click="onAction" />
import { ref } from "vue";
import type { NFabItem } from "@/uni_modules/new-ui/components/n-fab/props";
const active = ref(false);
const actions: NFabItem[] = [
{ icon: "star", label: "收藏" },
{ abbr: "赞", label: "点赞", textSize: 22, background: "#f59e0b" },
{ src: "/static/avatar.png", label: "联系人", imageWidth: 32, imageHeight: 32, imageRadius: 8 }
];
function onAction(detail: { index: number }): void {
uni.showToast({ title: `点击 ${detail.index}`, icon: "none" });
}
状态与定位
active 为受控值,使用官方 v-model:active。点击主按钮请求切换状态;点击菜单项先触发 click({index}),再请求收起;遮罩仅在 maskClosable=true 时请求收起。父组件不回写更新请求时,状态保持原值。无菜单或 expandable=false 时,主按钮直接触发 click({index:-1})。
position 始终相对可用视口定位主按钮,菜单不会挤动主按钮;Web 计入导航栏和底部栏的视口偏移。direction 单独控制展开方向;请选择有足够空间的方向,例如右下角向上或向左。组件不会擅自改变指定方向。
draggable=auto 拖动后吸附最近的左右边缘;free 保留松手位置;none 关闭拖动。实际位移超过 10px 才进入拖拽,轻触不会触发吸附;取消手势保留当前位置,不吸附。Web 同时支持鼠标拖动。按钮尺寸来自组件内部的真实布局测量,调用者不需要查询元素、监听滚动或手动刷新。尺寸、角位、间距及窗口变化后重新按配置定位。gap 顺序为上、右、下、左,每项接受数字或 px/rpx 字符串;数字及无单位字符串按 px 解释。定位和吸附共用官方单位换算后的像素值,rpx 遵循平台转换规则和宿主的单位配置。窗口变化及隐藏后恢复时刷新视口并重新取转换结果;底部需要避开系统手势区域时增大第三项。
展开时从遮罩、菜单项、标签和自定义菜单滑动均在组件共同根节点处理,不会滚动底层页面;Web 在遮罩可见时同时拦截组件区域内的滚轮。收起后底层页面正常滚动。
内容与样式
圆内内容优先级为 icon > src > abbr,label 是朝屏幕中心一侧的标签。逐项颜色、字号、图片宽高与圆角直接写入列表数据。业务字段不同可配置 nameKey/srcKey/textKey,默认分别为 icon/src/label;组件内部完成映射,item 插槽仍取得原始数据及其业务附加字段。圆内缩写独立使用 abbr。例如 { text: "联系客服", abbr: "客" } 配合 textKey="text",侧边显示“联系客服”,圆内只显示“客”;不会把侧边标题作为缩写。background 同时作用于主按钮和没有独立背景的菜单项,支持实色及 linear-gradient(to right, #f53423, #f67f48);渐变方向请使用关键词。
NFabLength 即 number | string。size=-1 与 iconSize=-1 使用主题令牌,实际默认分别为 56px、16px;显式尺寸覆盖令牌;主按钮尺寸、逐项字号、图片宽高和圆角均接受数字或 px/rpx 字符串,例如 icon-size="32rpx"。尺寸字段中的 -1 表示采用主题值,圆角可为 0;边距允许 0,不采用 -1。customClass/customStyle 作用于组件根容器,适合设置 CSS 变量;内部按钮背景请用 background,不依赖样式继承。
插槽
default 替换整区菜单,trigger 替换主按钮内容,item 替换列表项圆内内容。自定义内容不叠加旋转。default 菜单中的点击与收起由插槽内容决定:
disabled 同时禁用内置内容与所有插槽,组件在交互区域拦截触摸和点击,调用者无需再给插槽按钮绑定禁用属性。插槽内容具有独立的层叠边界,内部设置 z-index 不会越过组件的禁用交互层。禁用保留 active,已展开的菜单继续显示并降低透明度;恢复后保持原展开状态。列表项 disabled 同样覆盖该项的 item 插槽;整体与单项同时禁用时不重复降低透明度。
<n-fab v-model:active="active">
<template #default>
<n-button text="新建" @click="active = false" />
</template>
<template #trigger>
<n-icon name="edit-fill" :size="24" color="#ffffff" />
</template>
</n-fab>
微信开放能力
FAB 可作为客服、分享或授权入口。开放能力是可选的原生按钮能力,默认 openType="" 不启用;App 和 Web 不编译该能力层。主按钮和列表项均可配置能力参数,支持的平台值与按钮一致:contact、share、getPhoneNumber、getRealtimePhoneNumber、getUserInfo、launchApp、openSetting、chooseAvatar、agreePrivacyAuthorization、feedback、liveActivity。除 share/feedback 使用平台自身流程,其余能力均透传对应原生回调和原生 error。原生能力按钮只在微信端编译,点击仍经过组件的 click/change 逻辑;普通轻触保留能力,实际拖动后屏蔽该次点击。禁用时清空 open-type 并拦截组件操作。
API
属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| list | NFabItem[] | [] | 菜单项列表(NFabItem);无 default 插槽且为空数组时为单按钮模式,点击主按钮触发 click({index:-1}) |
| nameKey | string | icon | 菜单数据的图标字段名;映射在组件内完成,不修改原始数据 |
| srcKey | string | src | 菜单数据的图片地址字段名 |
| textKey | string | label | 菜单数据的侧边标签字段名;圆内缩写独立使用 abbr 字段 |
| active | boolean | false | 展开状态,v-model:active 双向绑定;展开时显示菜单与遮罩,收起时主按钮图标复位 |
| type | NFabType | primary | 主按钮色调:primary / success / warning / error / info,菜单项默认跟随 |
| background | string | 主按钮与菜单项背景,支持实色及关键词方向的 linear-gradient;菜单项可逐项覆盖 | |
| position | NFabPosition | right-bottom | 停驻角位:right-bottom / left-bottom / right-top / left-top;展开方向须留有空间 |
| direction | NFabDirection | top | 菜单展开方向:top 向上 / bottom 向下(列),left 向左 / right 向右(行) |
| draggable | NFabDraggable | auto | 拖拽模式:auto 可拖并自动吸附左右边缘 / free 可拖停在松手处 / none 固定 |
| gap | NFabLength[] | [16, 16, 16, 16] | 与视口四边的间距,数字为 px,字符串支持 px/rpx;顺序 [上, 右, 下, 左];悬浮在底部时注意加大下边距避开手势条 |
| size | NFabLength | -1 | 主按钮与菜单项直径:数字或 px/rpx 字符串;-1 使用主题尺寸(默认 56px) |
| iconSize | NFabLength | -1 | 主按钮与菜单项图标尺寸:数字或 px/rpx 字符串;-1 使用主题尺寸(默认 16px) |
| inactiveIcon | string | plus | 收起态主按钮图标名 |
| activeIcon | string | close | 展开态主按钮图标名 |
| iconColor | string | 主按钮与菜单项的图标/文字颜色覆盖(空 = 白色) | |
| expandable | boolean | true | 点击主按钮是否展开菜单;false 时主按钮为普通动作按钮,点击触发 click(-1) |
| disabled | boolean | false | 禁用整个组件(含插槽内容):保色相降透明,点击与拖拽被拦截,保留 active 受控值 |
| zIndex | number | 99 | 悬浮层级;低于弹窗(1000 起),遮罩取 zIndex - 10 |
| mask | boolean | true | 展开时显示遮罩弱化页面 |
| maskClosable | boolean | false | 点击遮罩请求收起菜单 |
| maskBackground | string | 遮罩背景色;空使用主题遮罩色 | |
| openType | string | 主按钮微信开放能力(仅微信端生效),如 contact / share / getPhoneNumber;非空时主按钮点击走原生按钮承接 | |
| appParameter | string | 主按钮 open-type=launchApp 时打开 App 的参数 | |
| lang | string | en | 主按钮 open-type=getUserInfo 时返回信息的语言,合法值 zh_CN / zh_TW / en |
| sessionFrom | string | 主按钮 open-type=contact 时会话来源 | |
| sendMessageTitle | string | 主按钮 open-type=contact 时会话卡片标题 | |
| sendMessagePath | string | 主按钮 open-type=contact 时会话卡片点击进入的小程序路径 | |
| sendMessageImg | string | 主按钮 open-type=contact 时会话卡片图片 | |
| showMessageCard | boolean | false | 主按钮 open-type=contact 时显示会话内消息卡片 |
| phoneNumberNoQuotaToast | boolean | false | open-type=getPhoneNumber 失败时是否弹出原生提示,仅微信端生效 |
| customClass | string | 挂到根节点的额外 class(类须在组件样式表内定义,页面 class 打不进组件) | |
| customStyle | string | 根节点行内样式 |
事件
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| click | 点击菜单项或单按钮,主按钮 index 为 -1 | |
| change | 请求改变展开状态 | active: boolean |
| update:active | 配合 v-model:active 回写受控状态 | active: boolean |
| contact | 微信客服回调 | UniEvent |
| opensetting | 微信设置回调 | UniEvent |
| launchapp | 微信打开 App 回调 | UniEvent |
| getuserinfo | 微信用户信息回调 | UniEvent |
| getphonenumber | 微信手机号回调 | UniEvent |
| getrealtimephonenumber | 微信实时手机号回调 | UniEvent |
| chooseavatar | 微信头像选择回调 | UniEvent |
| agreeprivacyauthorization | 微信隐私授权回调 | UniEvent |
| createliveactivity | 微信活动订阅回调 | UniEvent |
| error | 配置/测量失败为 NFabError;微信开放能力失败为原生事件 | NFabError 或 UniEvent |
插槽
| 名称 | 说明 | 参数 |
|---|---|---|
| default | 整区自定义菜单;优先于 list | — |
| trigger | 自定义主按钮内容 | — |
| item | 自定义列表项圆内内容 |
数据与状态
NFabItem 为 UTSJSONObject,可携带业务附加字段。默认渲染字段如下;nameKey/srcKey/textKey 分别替换图标、图片和侧边标签的字段名。
| 字段 | 类型 | 说明 |
|---|---|---|
| icon / src / abbr | string | 圆内图标、图片、文字,按顺序取第一种非空内容 |
| label | string | 侧边标签 |
| background | string | 实色或渐变;空跟随主按钮 background/type |
| color | string | 圆内颜色;空跟随 iconColor,再回落主题填充前景色 |
| iconSize | number / string | 图标尺寸,数字或 px/rpx 字符串;-1 跟随全局 iconSize |
| textSize / labelSize | NFabLength | 圆内缩写/侧边标签字号,数字或 px/rpx 字符串;-1 跟随主题 |
| labelColor | string | 侧边标签颜色,空跟随主题 |
| imageWidth / imageHeight | NFabLength | 图片宽高,数字或 px/rpx 字符串;-1 跟随主题 |
| imageRadius | NFabLength | 图片圆角,数字或 px/rpx 字符串;-1 跟随主题,默认 0 |
| disabled | boolean | 禁用该项,保持色相并降低透明度 |
| openType / appParameter / lang / sessionFrom | string | 微信能力及参数 |
| sendMessageTitle / sendMessagePath / sendMessageImg | string | 微信会话卡片参数 |
| showMessageCard | boolean | 微信会话卡片开关 |
| phoneNumberNoQuotaToast | boolean | 获取手机号失败时显示原生提示,默认 false |
主题令牌
CSS 变量均有同名 $n- SCSS 覆盖变量:
| CSS 变量 | 默认值 | 作用 |
|---|---|---|
| --n-fab-size | 56px | 主按钮及菜单项直径 |
| --n-fab-icon-size | 16px | 主按钮及菜单图标尺寸 |
| --n-fab-menu-gap | 16px | 菜单项间距及菜单与主按钮距离 |
| --n-fab-label-size | 16px | 侧边标签字号 |
| --n-fab-text-size | 18px | 圆内文字字号 |
| --n-fab-image-width / --n-fab-image-height | 28px | 圆内图片宽高 |
| --n-fab-image-radius | 0px | 圆内图片圆角 |
| --n-fab-mask-color | rgba(0,0,0,0.5) | 遮罩色 |
| --n-fab-shadow-color | rgba(0,0,0,0.16) | 主按钮投影,深色主题自动覆盖 |
实现说明
| 机制 | 参考做法 | 本库做法及融合理由 |
|---|---|---|
| 菜单 | 任意默认插槽 / 配置列表 | 同时支持 default 与 list;item 用于单项内容定制 |
| 状态 | active 受控 / 内部展开状态 | 统一官方 v-model:active;点击只发更新请求,视觉直接由受控值决定 |
| 定位 | 四角与四边间距 / rpx 左右及底部距离 | 主按钮固定、菜单独立绝对定位;gap 保留数字 px 并支持 px/rpx 字符串,官方换算结果同时用于定位和吸附 |
| 拖拽 | auto/free/none / 跟手位移及边界限制 | 保留三态与视口限制;测量复用组件尺寸监听,微信采用带组件作用域的异步查询 |
| 内容 | 图标/图片/独立 abbr 缩写/可映射的侧边标签,逐项背景、字号与图片尺寸 | 保留 nameKey/srcKey/textKey 字段映射,在组件内归一为 icon/src/abbr/label;原始项交给插槽;textKey 只映射侧边标签,abbr 独立保存缩写;逐项颜色、字号和图片尺寸直接配置 |
| 单位 | rpx 字号、图片尺寸与圆角 / 数字及字符串按钮尺寸 | 所有尺寸统一支持数字 px、px/rpx 字符串;不要求调用者查询窗口或换算单位 |
| 滚动拦截 | 共同根节点拦截触摸移动 | 根节点覆盖遮罩、内置菜单和自定义菜单;Web 同时处理遮罩可见时的滚轮,清理随组件卸载 |
| 背景 | 可设置实色及渐变 | 保留 background 与逐项覆盖;沿用库内按钮的背景变量、种类类名及节点 key,避开不支持的动态 background 简写 |
| 遮罩 | mask、maskBackground、maskClosable=false | 全部保留;层级始终为 zIndex-10,动态关闭立即撤销拦截 |
| 图标 | 活跃/非活跃图标切换 / 同一 plus 旋转 | 采用状态图标切换,不叠加旋转;默认字号采用 16px,尺寸均可配置 |
| 动画 | 250ms 缩放淡入淡出 | 菜单常驻,受控状态直接切换 transform/opacity;没有等待挂载的固定延时及额外卸载补偿 |
| 标签 | 左侧停驻标签在右,右侧停驻标签在左 | 四向均沿用侧边标签,拖到另一侧后自动调整对齐;不增加无参考的下方标签形态 |
| 微信能力 | 主按钮及逐项原生按钮参数、能力回调 | 保留可选能力层,回调及手机号提示参数对齐按钮;仅微信编译,真实拖拽与普通点击分开处理,原生错误完整透传 |
| 动态内容 | 图标/图片/缩写及可选标签 | 标签存在性在行节点 key 中表达,内置内容种类与背景种类在圆钮 key 中表达;内容插槽保留原始数据,避免无关内容切换重建 |
| 禁用 | 点击守卫 / 原生能力置空 / 自定义操作区交互覆盖层 | 主按钮与菜单内容使用独立内容容器隔离层叠,插槽自带 z-index 仍位于组件交互层之下;主按钮、整区菜单和单项均拦截交互,避免插槽子组件先执行回调;整体透明度统一处理,单项禁用不叠加降透明 |
验收覆盖
| 功能面 | 回归检查 |
|---|---|
| 数据与 API | 33 项属性、13 项事件与元数据/文档一致;字段映射保留原项、标题与 abbr 分离,非法配置反馈及恢复 |
| 定位与手势 | 四角 × 四向、auto/free/none、拖拽阈值及取消、禁用中断、尺寸变化、窗口变化与恢复、rpx 边距换算及吸附 |
| 内容与样式 | 图标/图片/abbr、逐项尺寸及颜色、背景种类切换、主题令牌、动态标签、default/trigger/item 插槽;展开后禁用、单项禁用、无重复透明度及恢复交互 |
| 滚动与事件 | Web 实测菜单项/标签/遮罩/自定义菜单触摸及滚轮不穿透,收起后页面可滚动;微信校验原生 catchtouchmove 绑定及处理器注册;点击与拖拽区分、更新请求及微信回调透传 |
| 示例与交付 | 预览和复制代码保留字段及单位;复制内容可解析;元数据、规范审计、Web/微信开发者工具回归、Android/微信编译 |
微信自动化 SDK 的 CDP 触摸模拟会自行滚动页面,不能用它判定原生 catchtouchmove 是否拦截背景滚动;这一项尚未完成微信开发者工具的实际手势验证。