更新记录
1.0.0(2026-11-02)
下载此版本
- 初始版本。从
open_novel/content-management/windows/workTabBar.vue 解耦:
- 移除 vuex / vue-router / remix-icon 依赖,改为数据驱动(
items / activeKey 等 Props)。
- 导航与标签状态管理全部以事件外抛(
select / close / home / action)。
- 菜单项的禁用态由组件依据
items / activeKey / 目标项纯逻辑计算。
- 样式仅使用 uni 官方
$uni-* 令牌,无硬编码色值。
- 首页 / 更多 / 关闭 / 菜单图标均提供具名插槽,默认用 Unicode 字形兜底。
- 横向滚动承载层改为内部 inline-flex,规避 H5 下 scroll-view 块级包裹导致换行的问题。
平台兼容性
uni-app(3.8.12)
| Vue2 |
Vue3 |
Chrome |
Safari |
app-vue |
app-nvue |
Android |
iOS |
鸿蒙 |
| - |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
| 微信小程序 |
支付宝小程序 |
抖音小程序 |
百度小程序 |
快手小程序 |
京东小程序 |
鸿蒙元服务 |
QQ小程序 |
飞书小程序 |
小红书小程序 |
快应用-华为 |
快应用-联盟 |
| √ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
u-work-tab-bar
工作标签页栏(类浏览器多标签页)。从 open_novel/content-management/windows/workTabBar.vue
解耦而来:数据驱动 + 事件外抛,零业务依赖——不耦合 vuex / vue-router / remix-icon,
不内置任何跳转逻辑,导航与标签状态管理全部由消费方负责。
特性
- 可横向滚动的已打开标签列表,激活标签自动滚动到可视区(
scroll-into-view)。
- 单个标签可关闭(非固定 + 多标签时显示关闭按钮)。
- 标签支持「固定」:固定标签不可关闭,且不受「关闭其他 / 关闭全部」影响。
- 首页按钮、更多按钮独立可配。
- 右键标签 / 点击「更多」弹出操作菜单:刷新、固定、关闭左侧 / 右侧 / 其他 / 全部;
菜单项的禁用态由组件依据
items / activeKey / 目标项纯逻辑计算。
- 样式仅用 uni 官方
$uni-* 令牌,无硬编码色值(样式抽离到 u-work-tab-bar.scss)。
- 图标默认用
vant-icon(内置字体图标,apps-o / arrow-down / cross 等),与原始 workTabBar.vue 的 remix 语义一一对应;全部通过具名插槽可替换为任意图标(remix-icon / uni-icons 等)。
- 类型契约抽离到
props.ts(WorkTabItem / WorkTabMenuKey / WorkTabMenuItem / WorkTabBarProps),与 vant-* 组件拆分约定一致。
文件结构
u-work-tab-bar/
├── components/u-work-tab-bar/
│ ├── u-work-tab-bar.vue # 模板 + 逻辑(import 自 props.ts,样式 src 自 .scss)
│ ├── props.ts # 对外类型契约
│ └── u-work-tab-bar.scss # 全部样式(仅用 $uni-* 令牌)
├── package.json
├── readme.md
└── changelog.md
Props
| 属性 |
类型 |
默认值 |
说明 |
items |
WorkTabItem[] |
[] |
已打开标签列表 |
activeKey |
string |
'' |
当前激活标签的 key |
showHome |
boolean |
true |
是否展示首页按钮 |
showMore |
boolean |
true |
是否展示更多按钮 |
homeTitle |
string |
'首页' |
首页按钮文案 / aria |
moreTitle |
string |
'更多' |
更多按钮文案 / aria |
menu |
WorkTabMenuKey[] |
['refresh','pin','left','right','other','all'] |
弹出菜单展示的操作项(可裁剪) |
closableDefault |
boolean |
true |
非固定标签是否默认显示关闭按钮 |
WorkTabItem:{ key: string; title: string; fixed?: boolean; closable?: boolean }
WorkTabMenuKey:'refresh' | 'pin' | 'left' | 'right' | 'other' | 'all'
Events
| 事件 |
载荷 |
说明 |
select |
key: string |
点击标签 |
close |
key: string |
点击标签关闭按钮 |
home |
— |
点击首页按钮 |
action |
{ action: WorkTabMenuKey; key: string } |
点击弹出菜单项(refresh/pin/left/right/other/all) |
刷新 / 固定 / 关闭类操作均只抛出语义事件,由消费方对接 vuex mutation、路由跳转、
页面刷新等业务逻辑。组件本身不读写 store、不调用 uni.redirectTo。
Slots
| 插槽名 |
作用域 |
默认内容 |
说明 |
home |
— |
vant-icon apps-o |
首页 / 仪表盘按钮图标 |
more |
— |
vant-icon arrow-down |
更多按钮图标 |
close |
{ item } |
vant-icon cross |
单个标签关闭按钮图标 |
menu-icon |
{ actionKey, label, disabled } |
vant-icon 按 action 映射 |
弹出菜单项图标:refresh→replay / pin→bookmark-o / left→arrow-left / right→arrow / other→cross / all→close |
菜单定位
- 两种模式统一为
absolute,定位父级都是 .u-work-tab-bar__more-wrap(position: relative)。
- 「更多」菜单由 纯 CSS 锚定:
top: calc(100% + 6px); right: 0 —— 恒在标签栏正下方、右缘与更多按钮对齐,零测量。
- 右键菜单锚到被右键标签的正下方、右缘与该标签对齐:先用
getBoundingClientRect() 在视口坐标里算好位置(含防溢出与左右翻转),
再按锚点容器的视口矩形与缩放比(rect.width / offsetWidth)换算成局部坐标写入内联样式。
因此页面滚动、祖先 transform、预览缩放都不会让菜单漂移。
- 防溢出规则:菜单底部超出视口时只上移到标签下沿为止,绝不高于标签栏(矮视口下宁可向下溢出),
避免“弹窗跑到标签上方”遮住标签栏与页面头部。
- ⚠️ 菜单是绝对定位元素,祖先
overflow: hidden 会裁剪菜单。若标签栏放在带裁剪的容器(如圆角浮窗)里,
请让该容器 overflow: visible,或保证菜单的展开区域在容器内。
- 菜单项图标与文字共用 18px 行盒,并对
vant-icon 字形墨迹的 1px 上偏做补偿,保证图标与文字视觉中心重合。
关闭按钮
- 关闭按钮为 16×16 圆形(hover 显示浅底),
padding 让图标与圆边留白,可按需调整。
- 图标(默认
vant-icon cross)以 absolute + top/left 50% + translate(-50%, -50%) 落在圆心,
不受 padding、图标盒子尺寸与字体基线影响。
- 默认
opacity: 0.6(vant-icon 自带行内 color,故用 opacity 收敛),hover 恢复满色。
用法示例
<template>
<u-work-tab-bar
:items="tabs"
:active-key="activeKey"
@select=""
@close="onClose"
@home="onHome"
@action="onAction"
>
<!-- 可选:用 remix-icon / uni-icons 替换默认 vant-icon(不传插槽则显示内置 vant-icon) -->
<template #home><remix-icon name="dashboard-line" :size="18" /></template>
<template #more><remix-icon name="arrow-down-s-line" :size="20" /></template>
<template #close><remix-icon name="close-large-fill" :size="10" /></template>
<template #menu-icon="{ actionKey }">
<remix-icon :name="menuIconMap[actionKey]" :size="15" />
</template>
</u-work-tab-bar>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const tabs = ref([
{ key: '/', title: '工作台', fixed: true },
{ key: '/order/list', title: '订单列表' },
{ key: '/user/detail', title: '用户详情' },
])
const activeKey = ref('/order/list')
const menuIconMap: Record<string, string> = {
refresh: 'refresh-line',
pin: 'pushpin-2-line',
left: 'arrow-left-s-line',
right: 'arrow-right-s-line',
other: 'close-fill',
all: 'close-circle-line',
}
function (key: string) {
// 业务:跳转 / 激活对应页
}
function onClose(key: string) {
// 业务:从 store 移除该标签,必要时跳转到相邻标签
}
function onHome() {
// 业务:回到首页
}
function onAction({ action, key }: { action: string; key: string }) {
// 业务:刷新 / 固定 / 关闭左·右·其他·全部
}
</script>