fix: 将侧栏提示层移到导航外侧
将桌面折叠侧栏的 NPopover 显式挂载到 body,并把提示框完整偏移到 64px 导航边界之外。
This commit is contained in:
+25
-25
@@ -1,42 +1,41 @@
|
|||||||
# Design QA
|
# Design QA
|
||||||
|
|
||||||
- Source visual truth: `/workspace/scratch/6e52996114b8/upload/6c29987c-43a6-4ded-ba86-b66d1c638f51.png`
|
- Source visual truth: `/workspace/scratch/6e52996114b8/upload/28e7be72-5b24-4b86-bae5-d0d4eb1015e2.png`
|
||||||
- Source pixel dimensions: `151 × 506`.
|
- Source pixel dimensions: `151 × 381`.
|
||||||
- Browser-rendered implementation: `/workspace/scratch/6e52996114b8/sidebar-popover-pc-final-20260909.jpg`
|
- Browser-rendered implementation: `/workspace/scratch/6e52996114b8/sidebar-popover-outside-20260909.jpg`
|
||||||
- Browser-rendered implementation dimensions: `1363 × 936`.
|
- Browser-rendered implementation dimensions: `1363 × 936`.
|
||||||
- Focused side-by-side comparison: `/workspace/scratch/6e52996114b8/sidebar-popover-comparison-final-20260909.jpg`
|
- Focused side-by-side comparison: `/workspace/scratch/6e52996114b8/sidebar-popover-outside-comparison-20260909.jpg`
|
||||||
- Implementation CSS viewport: `1363 × 936`, device scale factor `1`.
|
- Implementation CSS viewport: `1363 × 936`, device scale factor `1`.
|
||||||
- State: PC dark theme, collapsed sidebar, pointer hovering the disabled “视觉风格” icon.
|
- State: PC dark theme, collapsed sidebar, pointer hovering the disabled “视觉风格” icon.
|
||||||
|
|
||||||
## Full-view comparison evidence
|
## Full-view comparison evidence
|
||||||
|
|
||||||
The implementation preserves the existing collapsed sidebar width, icon order, selected state and main-content boundary. Hovering an icon opens a compact Naive UI popover to the right without shifting the sidebar or content.
|
The browser render keeps the `64px` collapsed navigation rail unchanged. The visible popover is teleported to `body`, rendered above the content layer, and no longer begins inside the sidebar boundary.
|
||||||
|
|
||||||
## Focused region comparison evidence
|
## Focused region comparison evidence
|
||||||
|
|
||||||
The focused comparison places the supplied sidebar crop beside a `320 × 506` crop of the browser render. The source icon spacing, dark surfaces and green selected state remain unchanged. The implementation adds the requested hover label beside the icon; disabled workflow items use the explanatory suffix “(剧本完成后可用)”.
|
The focused comparison places the supplied crop beside a `320 × 381` crop of the revised browser render. The source's red annotation identifies the content-side area beside the collapsed rail. Browser geometry confirms the sidebar ends at `x = 64px` and the popover body begins at `x = 65px`, leaving the body fully outside the sidebar while its arrow still points back to the icon.
|
||||||
|
|
||||||
## Required fidelity surfaces
|
## Required fidelity surfaces
|
||||||
|
|
||||||
- Typography: the application typography is unchanged; popover copy uses the current Naive UI theme and compact UI text sizing.
|
- Typography: existing application and Naive UI popover typography are unchanged.
|
||||||
- Spacing and layout rhythm: the `64px` collapsed sidebar and existing vertical menu rhythm are preserved. Popovers use `placement="right"` and do not alter document flow.
|
- Spacing and layout rhythm: the `64px` sidebar and icon rhythm remain unchanged. The popover's total inline-start margin is `24px`, producing a measured `1px` gap outside the sidebar edge.
|
||||||
- Colors and visual tokens: dark sidebar surfaces and the selected green state remain unchanged. Popovers inherit the existing dark Naive UI theme.
|
- Colors and visual tokens: dark surfaces, selected green state and popover theme tokens remain unchanged.
|
||||||
- Image and asset fidelity: no raster assets were introduced. Existing Lucide menu icons are preserved.
|
- Image and asset fidelity: no raster or icon assets were added or modified.
|
||||||
- Copy and content: PC hints use each menu's existing label. Locked entries append “(剧本完成后可用)”; “后端连接” keeps its original copy.
|
- Copy and content: menu labels and locked-state explanation remain unchanged.
|
||||||
|
|
||||||
## Findings
|
## Findings
|
||||||
|
|
||||||
- No actionable P0/P1/P2 visual or responsive issues remain.
|
- No actionable P0/P1/P2 visual or responsive issues remain.
|
||||||
- The disconnected local backend produces the expected in-page `502` response; it does not affect sidebar layout or popover behavior.
|
- The disconnected local backend produces the expected in-page `502` response; it does not affect popover positioning.
|
||||||
|
|
||||||
## Primary interactions tested
|
## Primary interactions tested
|
||||||
|
|
||||||
- Collapsed the PC sidebar and hovered an enabled menu icon; “我的剧本” appeared.
|
- Collapsed the PC sidebar and hovered the “视觉风格” icon.
|
||||||
- Hovered the disabled “视觉风格” icon; its availability explanation appeared.
|
- Confirmed the popover content remained readable and the arrow still targeted the icon.
|
||||||
- Hovered the bottom settings icon; “后端连接” appeared.
|
- Measured the sidebar and popover body bounds to confirm the popover starts outside the sidebar.
|
||||||
- Clicked an enabled collapsed icon and confirmed navigation to the target workflow route.
|
- Confirmed the popover is explicitly mounted to `body` with a high overlay level.
|
||||||
- Expanded the PC sidebar and confirmed menu popovers were absent.
|
- Confirmed component tests continue to cover desktop-only popover rendering.
|
||||||
- Confirmed through component tests that mobile navigation does not render menu popovers.
|
|
||||||
|
|
||||||
## Console errors checked
|
## Console errors checked
|
||||||
|
|
||||||
@@ -44,15 +43,16 @@ No application-origin console errors were reported. The cloud browser logged onl
|
|||||||
|
|
||||||
## Comparison history
|
## Comparison history
|
||||||
|
|
||||||
- Iteration 1: added native NPopover wrappers and found that the full-width transparent RouterLink intercepted hover on enabled menu icons.
|
- Iteration 1: the popover body began at `x = 51px` while the sidebar ended at `x = 64px`, leaving a `13px` overlap (P2).
|
||||||
- Iteration 2: made enabled collapsed icons direct RouterLinks, raised their trigger layer above the transparent label and rechecked enabled, disabled and settings hints in the browser.
|
- Fix: mounted the popover explicitly to `body`, raised its overlay level, and overrode Naive UI's default inline margin in the component style.
|
||||||
|
- Iteration 2: the revised browser render measured the popover body at `x = 65px`, a `1px` gap outside the sidebar edge, with no remaining P0/P1/P2 issue.
|
||||||
|
|
||||||
## Implementation checklist
|
## Implementation checklist
|
||||||
|
|
||||||
- [x] Add NPopover hints to all PC collapsed sidebar menu icons.
|
- [x] Place the PC sidebar popover outside the collapsed rail.
|
||||||
- [x] Preserve click navigation for enabled collapsed icons.
|
- [x] Keep the arrow aligned with the hovered icon.
|
||||||
- [x] Explain locked workflow items in their hint copy.
|
- [x] Prevent business content from covering the popover.
|
||||||
- [x] Keep PC expanded and mobile navigation free of duplicate popovers.
|
- [x] Keep existing sidebar spacing, colors, icons and copy.
|
||||||
- [x] Preserve sidebar size, spacing, theme and selected state.
|
- [x] Preserve desktop-only popover behavior.
|
||||||
|
|
||||||
final result: passed
|
final result: passed
|
||||||
|
|||||||
+19
-2
@@ -127,7 +127,13 @@ function createMenuIcon(icon: typeof FolderOpen, label: string, popoverEnabled:
|
|||||||
|
|
||||||
return h(
|
return h(
|
||||||
NPopover,
|
NPopover,
|
||||||
{ trigger: 'hover', placement: 'right' },
|
{
|
||||||
|
trigger: 'hover',
|
||||||
|
placement: 'right',
|
||||||
|
to: 'body',
|
||||||
|
class: 'admin-nav-popover',
|
||||||
|
zIndex: 3000
|
||||||
|
},
|
||||||
{
|
{
|
||||||
trigger,
|
trigger,
|
||||||
default: () => label
|
default: () => label
|
||||||
@@ -239,7 +245,14 @@ const menuOptions = computed<MenuOption[]>(() => {
|
|||||||
</NScrollbar>
|
</NScrollbar>
|
||||||
<!-- 以下是侧栏底部后端连接:折叠裁切文字,图标位置保持不变 -->
|
<!-- 以下是侧栏底部后端连接:折叠裁切文字,图标位置保持不变 -->
|
||||||
<footer class="admin-sider-footer">
|
<footer class="admin-sider-footer">
|
||||||
<NPopover trigger="hover" placement="right" :disabled="narrow || !collapsed">
|
<NPopover
|
||||||
|
trigger="hover"
|
||||||
|
placement="right"
|
||||||
|
to="body"
|
||||||
|
class="admin-nav-popover"
|
||||||
|
:z-index="3000"
|
||||||
|
:disabled="narrow || !collapsed"
|
||||||
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<NButton
|
<NButton
|
||||||
quaternary
|
quaternary
|
||||||
@@ -368,6 +381,10 @@ const menuOptions = computed<MenuOption[]>(() => {
|
|||||||
.admin-menu-popover-trigger {
|
.admin-menu-popover-trigger {
|
||||||
@apply relative z-[1] inline-flex items-center justify-center text-inherit;
|
@apply relative z-[1] inline-flex items-center justify-center text-inherit;
|
||||||
}
|
}
|
||||||
|
/* 折叠侧栏提示完整落在 64px 边界之外,并覆盖业务内容层。 */
|
||||||
|
.n-popover.admin-nav-popover {
|
||||||
|
margin-inline-start: 24px !important;
|
||||||
|
}
|
||||||
.admin-brand {
|
.admin-brand {
|
||||||
@apply flex shrink-0 items-center gap-3 h-[74px] overflow-hidden py-0 px-5 text-ink whitespace-nowrap;
|
@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);
|
min-width: var(--admin-sider-expanded);
|
||||||
|
|||||||
@@ -178,7 +178,19 @@ describe('管理后台组件边界', () => {
|
|||||||
expect(wrapper.find('[aria-label="展开侧栏"]').exists()).toBe(true)
|
expect(wrapper.find('[aria-label="展开侧栏"]').exists()).toBe(true)
|
||||||
expect(wrapper.getComponent(NMenu).props('collapsed')).toBe(false)
|
expect(wrapper.getComponent(NMenu).props('collapsed')).toBe(false)
|
||||||
expect(firstMenuIcon().type).toBe(NPopover)
|
expect(firstMenuIcon().type).toBe(NPopover)
|
||||||
expect(wrapper.get('.admin-sider-footer').getComponent(NPopover).props('disabled')).toBe(false)
|
expect(firstMenuIcon().props).toMatchObject({
|
||||||
|
placement: 'right',
|
||||||
|
to: 'body',
|
||||||
|
class: 'admin-nav-popover',
|
||||||
|
zIndex: 3000
|
||||||
|
})
|
||||||
|
const footerPopover = wrapper.get('.admin-sider-footer').getComponent(NPopover)
|
||||||
|
expect(footerPopover.props()).toMatchObject({
|
||||||
|
disabled: false,
|
||||||
|
placement: 'right',
|
||||||
|
to: 'body',
|
||||||
|
zIndex: 3000
|
||||||
|
})
|
||||||
const settings = wrapper.get('.admin-sider-footer [aria-label="后端连接"]')
|
const settings = wrapper.get('.admin-sider-footer [aria-label="后端连接"]')
|
||||||
expect(settings.text()).toBe('后端连接')
|
expect(settings.text()).toBe('后端连接')
|
||||||
await settings.trigger('click')
|
await settings.trigger('click')
|
||||||
@@ -195,6 +207,7 @@ describe('管理后台组件边界', () => {
|
|||||||
)
|
)
|
||||||
expect(css).toMatch(/\.admin-sider\.is-collapsed \.n-menu-item-content-header\s*\{[^}]*opacity:\s*0/)
|
expect(css).toMatch(/\.admin-sider\.is-collapsed \.n-menu-item-content-header\s*\{[^}]*opacity:\s*0/)
|
||||||
expect(css).toMatch(/\.admin-sider\.is-collapsed \.n-menu-item-content__icon\s*\{[^}]*z-index:\s*2/)
|
expect(css).toMatch(/\.admin-sider\.is-collapsed \.n-menu-item-content__icon\s*\{[^}]*z-index:\s*2/)
|
||||||
|
expect(css).toMatch(/\.n-popover\.admin-nav-popover\s*\{[^}]*margin-inline-start:\s*24px\s*!important/)
|
||||||
expect(css).toMatch(/\.admin-sider-footer\s*\{[^}]*flex-shrink:\s*0/)
|
expect(css).toMatch(/\.admin-sider-footer\s*\{[^}]*flex-shrink:\s*0/)
|
||||||
expect(css).toMatch(/\.admin-sider\.is-collapsed \.admin-settings-label\s*\{[^}]*opacity:\s*0/)
|
expect(css).toMatch(/\.admin-sider\.is-collapsed \.admin-settings-label\s*\{[^}]*opacity:\s*0/)
|
||||||
expect(css).toMatch(/\.admin-sider\.is-collapsed \.admin-brand span\s*\{[^}]*opacity:\s*0/)
|
expect(css).toMatch(/\.admin-sider\.is-collapsed \.admin-brand span\s*\{[^}]*opacity:\s*0/)
|
||||||
|
|||||||
Reference in New Issue
Block a user