feat: 为桌面折叠侧栏添加菜单提示

PC 折叠侧栏菜单图标增加 NPopover 提示,并保留图标导航能力;展开态和移动端不显示重复提示。
This commit is contained in:
GouJ
2026-09-09 10:18:02 +08:00
committed by GitHub
parent 6dc3a93348
commit c2639ca7af
3 changed files with 117 additions and 60 deletions
+70 -27
View File
@@ -8,8 +8,8 @@ import {
NLayout,
NLayoutHeader,
NMenu,
NPopover,
NScrollbar,
NTooltip,
zhCN,
dateZhCN,
type MenuOption
@@ -49,6 +49,10 @@ const collapsed = ref(narrow.value)
const settingsOpen = ref(false)
/** 桌面折叠只裁切文字;移动端侧栏打开时始终显示完整菜单。 */
const menuCollapsed = false
/** 菜单内部会缓存图标 VNode,切换显示模式时重建一次以同步 Popover 状态。 */
const menuRenderKey = computed(
() => `${narrow.value ? 'mobile' : 'desktop'}-${collapsed.value ? 'collapsed' : 'expanded'}`
)
/** Naive Menu 的桌面折叠宽度;移动端覆盖层不参与正文宽度计算。 */
const siderCollapsedWidth = SIDER_COLLAPSED_WIDTH
/** 侧栏展开宽度仅用于桌面布局。 */
@@ -111,31 +115,62 @@ const workflowItems = [
['storyboard', '分镜设计', Camera],
['production', '镜头生产', Clapperboard]
] as const
/** PC 折叠侧栏只显示图标时渲染 Popover;可用图标同时保留路由跳转能力。 */
function createMenuIcon(icon: typeof FolderOpen, label: string, popoverEnabled: boolean, target?: string) {
return () => {
const trigger = () =>
popoverEnabled && target
? h(RouterLink, { to: target, class: 'admin-menu-popover-trigger' }, () => h(icon, { size: 18 }))
: h('span', { class: 'admin-menu-popover-trigger' }, [h(icon, { size: 18 })])
if (!popoverEnabled) return trigger()
return h(
NPopover,
{ trigger: 'hover', placement: 'right' },
{
trigger,
default: () => label
}
)
}
}
/** 菜单链接使用 RouterLink,保留新标签打开和浏览器导航行为。 */
const menuOptions = computed<MenuOption[]>(() => [
{
key: '/projects',
label: () => h(RouterLink, { to: '/projects' }, () => '我的剧本'),
icon: () => h(FolderOpen, { size: 18 })
},
...(projectId.value
? [
{ type: 'divider' as const, key: 'divider' },
...workflowItems.map(([path, label, icon]) => {
const disabled = path !== 'create-drama' && !workflowsUnlocked.value
return {
key: '/projects/' + projectId.value + '/' + path,
disabled,
label: () =>
disabled
? h('span', { title: '剧本完成后可用', 'aria-disabled': 'true' }, label)
: h(RouterLink, { to: '/projects/' + projectId.value + '/' + path }, () => label),
icon: () => h(icon, { size: 18 })
}
})
]
: [])
])
const menuOptions = computed<MenuOption[]>(() => {
// 仅在 PC 折叠态创建 Popover,避免展开态和移动端出现重复提示。
const popoverEnabled = !narrow.value && collapsed.value
return [
{
key: '/projects',
label: () => h(RouterLink, { to: '/projects' }, () => '我的剧本'),
icon: createMenuIcon(FolderOpen, '我的剧本', popoverEnabled, '/projects')
},
...(projectId.value
? [
{ type: 'divider' as const, key: 'divider' },
...workflowItems.map(([path, label, icon]) => {
const disabled = path !== 'create-drama' && !workflowsUnlocked.value
const target = '/projects/' + projectId.value + '/' + path
return {
key: target,
disabled,
label: () =>
disabled
? h('span', { title: '剧本完成后可用', 'aria-disabled': 'true' }, label)
: h(RouterLink, { to: target }, () => label),
icon: createMenuIcon(
icon,
disabled ? `${label}(剧本完成后可用)` : label,
popoverEnabled,
disabled ? undefined : target
)
}
})
]
: [])
]
})
</script>
<template>
@@ -191,6 +226,7 @@ const menuOptions = computed<MenuOption[]>(() => [
</header>
<NScrollbar class="admin-nav-scroll">
<NMenu
:key="menuRenderKey"
:value="route.path"
:options="menuOptions"
:collapsed="menuCollapsed"
@@ -203,7 +239,7 @@ const menuOptions = computed<MenuOption[]>(() => [
</NScrollbar>
<!-- 以下是侧栏底部后端连接折叠裁切文字图标位置保持不变 -->
<footer class="admin-sider-footer">
<NTooltip placement="right" :disabled="!collapsed">
<NPopover trigger="hover" placement="right" :disabled="narrow || !collapsed">
<template #trigger>
<NButton
quaternary
@@ -218,7 +254,7 @@ const menuOptions = computed<MenuOption[]>(() => [
</NButton>
</template>
后端连接
</NTooltip>
</NPopover>
</footer>
</aside>
<!-- 以下是顶栏与主内容 -->
@@ -304,6 +340,10 @@ const menuOptions = computed<MenuOption[]>(() => [
.admin-sider.is-collapsed .n-menu-item-content-header {
opacity: 0;
}
/* 折叠态将图标抬到透明路由链接上方,使点击与悬停都命中图标触发区。 */
.admin-sider.is-collapsed .n-menu-item-content__icon {
@apply relative z-[2];
}
.admin-sider-footer {
@apply w-full shrink-0 overflow-hidden;
padding-block: 8px max(8px, env(safe-area-inset-bottom));
@@ -325,6 +365,9 @@ const menuOptions = computed<MenuOption[]>(() => [
.admin-sider .n-menu .n-menu-item-content::before {
@apply left-0 right-0 rounded-none;
}
.admin-menu-popover-trigger {
@apply relative z-[1] inline-flex items-center justify-center text-inherit;
}
.admin-brand {
@apply flex shrink-0 items-center gap-3 h-[74px] overflow-hidden py-0 px-5 text-ink whitespace-nowrap;
min-width: var(--admin-sider-expanded);