feat: 为桌面折叠侧栏添加菜单提示
PC 折叠侧栏菜单图标增加 NPopover 提示,并保留图标导航能力;展开态和移动端不显示重复提示。
This commit is contained in:
+70
-27
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user